diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image/internal_image_error.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image/internal_image_error.rs index fae3383af2..5f9e69c16c 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image/internal_image_error.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image/internal_image_error.rs @@ -1,8 +1,9 @@ +use crate::core::artcraft_error::ArtcraftError; use base64::DecodeError; use errors::AnyhowError; use fal_client::error::fal_error_plus::FalErrorPlus; use openai_sora_client::sora_error::SoraError; -use storyteller_client::error::api_error::ApiError; +use storyteller_client::error::storyteller_error::StorytellerError; #[derive(Debug)] pub enum InternalImageError { @@ -12,9 +13,10 @@ pub enum InternalImageError { FalError(FalErrorPlus), SoraError(SoraError), AnyhowError(AnyhowError), - StorytellerApiError(ApiError), + StorytellerError(StorytellerError), DecodeError(DecodeError), IoError(std::io::Error), + ArtcraftError(ArtcraftError), } impl From for InternalImageError { @@ -35,9 +37,9 @@ impl From for InternalImageError { } } -impl From for InternalImageError { - fn from(value: ApiError) -> Self { - Self::StorytellerApiError(value) +impl From for InternalImageError { + fn from(value: StorytellerError) -> Self { + Self::StorytellerError(value) } } @@ -52,3 +54,15 @@ impl From for InternalImageError { Self::IoError(value) } } + +impl From for InternalImageError { + fn from(value: ArtcraftError) -> Self { + match value { + ArtcraftError::AnyhowError(e) => Self::AnyhowError(e), + ArtcraftError::DecodeError(e) => Self::DecodeError(e), + ArtcraftError::IoError(e) => Self::IoError(e), + ArtcraftError::StorytellerError(e) => Self::StorytellerError(e), + _ => Self::ArtcraftError(value), + } + } +} diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/object/handle_object_fal.rs b/crates/desktop/artcraft/src/core/commands/enqueue/object/handle_object_fal.rs index eb26daa750..f3a6da71d3 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/object/handle_object_fal.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/object/handle_object_fal.rs @@ -34,7 +34,7 @@ pub async fn handle_object_fal( None => { error!("No FAL API key is set. Can't perform action."); let event = - GenerationEnqueueFailureEvent::no_fal_api_key(GenerationAction::GenerateImage); + GenerationEnqueueFailureEvent::no_fal_api_key(GenerationAction::ImageTo3d); if let Err(err) = event.send(&app) { error!("Failed to emit event: {:?}", err); // Fail open. diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/object/internal_object_error.rs b/crates/desktop/artcraft/src/core/commands/enqueue/object/internal_object_error.rs index a85886af73..7d410d94ba 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/object/internal_object_error.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/object/internal_object_error.rs @@ -1,9 +1,7 @@ use crate::core::artcraft_error::ArtcraftError; -use anyhow::anyhow; use base64::DecodeError; use errors::AnyhowError; use fal_client::error::fal_error_plus::FalErrorPlus; -use storyteller_client::error::api_error::ApiError; use storyteller_client::error::storyteller_error::StorytellerError; #[derive(Debug)] @@ -15,9 +13,9 @@ pub enum InternalObjectError { FalError(FalErrorPlus), AnyhowError(AnyhowError), StorytellerError(StorytellerError), - StorytellerApiError(ApiError), DecodeError(DecodeError), IoError(std::io::Error), + ArtcraftError(ArtcraftError), } impl From for InternalObjectError { @@ -32,9 +30,9 @@ impl From for InternalObjectError { } } -impl From for InternalObjectError { - fn from(value: ApiError) -> Self { - Self::StorytellerApiError(value) +impl From for InternalObjectError { + fn from(value: StorytellerError) -> Self { + Self::StorytellerError(value) } } @@ -57,9 +55,7 @@ impl From for InternalObjectError { ArtcraftError::DecodeError(e) => Self::DecodeError(e), ArtcraftError::IoError(e) => Self::IoError(e), ArtcraftError::StorytellerError(e) => Self::StorytellerError(e), - _ => { - Self::AnyhowError(anyhow!("Other error: {:?}", value)) - } + _ => Self::ArtcraftError(value), } } } diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/video/enqueue_image_to_video_command.rs b/crates/desktop/artcraft/src/core/commands/enqueue/video/enqueue_image_to_video_command.rs index 47b6127dc0..3d7088440f 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/video/enqueue_image_to_video_command.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/video/enqueue_image_to_video_command.rs @@ -1,5 +1,7 @@ use crate::core::commands::enqueue::object::handle_object_artcraft::handle_object_artcraft; use crate::core::commands::enqueue::object::handle_object_fal::handle_object_fal; +use crate::core::commands::enqueue::video::handle_video_artcraft::handle_video_artcraft; +use crate::core::commands::enqueue::video::handle_video_fal::handle_video_fal; use crate::core::commands::enqueue::video::internal_video_error::InternalVideoError; use crate::core::commands::response::failure_response_wrapper::{CommandErrorResponseWrapper, CommandErrorStatus}; use crate::core::commands::response::shorthand::Response; @@ -168,9 +170,9 @@ pub async fn handle_request( ) -> Result<(), InternalVideoError> { if fal_creds_manager.has_apparent_api_token()? { - handle_object_fal(&app, app_data_root, request, fal_creds_manager, fal_task_queue).await?; + handle_video_fal(&app, app_data_root, request, fal_creds_manager, fal_task_queue).await?; } else { - handle_object_artcraft(request, &app, app_data_root, storyteller_creds_manager).await?; + handle_video_artcraft(request, &app, app_data_root, storyteller_creds_manager).await?; } Ok(()) diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/video/handle_video_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/video/handle_video_artcraft.rs new file mode 100644 index 0000000000..fe347198bc --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/video/handle_video_artcraft.rs @@ -0,0 +1,136 @@ +use crate::core::commands::enqueue::image::enqueue_text_to_image_command::{EnqueueTextToImageModel, EnqueueTextToImageRequest}; +use crate::core::commands::enqueue::object::enqueue_image_to_3d_object_command::{EnqueueImageTo3dObjectModel, EnqueueImageTo3dObjectRequest}; +use crate::core::commands::enqueue::video::enqueue_image_to_video_command::{EnqueueImageToVideoModel, EnqueueImageToVideoRequest}; +use crate::core::commands::enqueue::video::internal_video_error::InternalVideoError; +use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; +use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; +use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent; +use crate::core::events::generation_events::generation_enqueue_success_event::GenerationEnqueueSuccessEvent; +use crate::core::state::data_dir::app_data_root::AppDataRoot; +use crate::core::utils::download_media_file_to_temp_dir::download_media_file_to_temp_dir; +use crate::core::utils::save_base64_image_to_temp_dir::save_base64_image_to_temp_dir; +use crate::services::fal::state::fal_credential_manager::FalCredentialManager; +use crate::services::fal::state::fal_task_queue::FalTaskQueue; +use crate::services::sora::state::sora_credential_manager::SoraCredentialManager; +use crate::services::sora::state::sora_task_queue::SoraTaskQueue; +use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; +use anyhow::anyhow; +use artcraft_api_defs::generate::object::generate_hunyuan_2_image_to_3d::GenerateHunyuan2ImageTo3dRequest; +use fal_client::creds::fal_api_key::FalApiKey; +use fal_client::requests::queue::enqueue_hunyuan2_image_to_3d::{enqueue_hunyuan2_image_to_3d, Hunyuan2Args}; +use fal_client::requests::queue::image_gen::enqueue_flux_pro_ultra_text_to_image::{enqueue_flux_pro_ultra_text_to_image, FluxProUltraTextToImageArgs}; +use fal_client::requests::queue::image_gen::enqueue_recraft3_text_to_image::{enqueue_recraft3_text_to_image, Recraft3TextToImageArgs}; +use idempotency::uuid::generate_random_uuid; +use log::{error, info, warn}; +use storyteller_client::generate::object::generate_hunyuan2_image_to_3d::generate_hunyuan2_image_to_3d; +use storyteller_client::utils::api_host::ApiHost; +use tauri::{AppHandle, State}; +use artcraft_api_defs::generate::video::generate_kling_1_6_image_to_video::GenerateKling16ImageToVideoRequest; +use storyteller_client::generate::video::generate_kling16_image_to_video::generate_kling16_image_to_video; + +pub async fn handle_video_artcraft( + request: EnqueueImageToVideoRequest, + app: &AppHandle, + app_data_root: &AppDataRoot, + storyteller_creds_manager: &StorytellerCredentialManager, +) -> Result<(), InternalVideoError> { + + let creds = match storyteller_creds_manager.get_credentials()? { + Some(creds) => creds, + None => { + error!("No Artcraft credentials are set. Can't perform action."); + let event = + GenerationEnqueueFailureEvent::no_artcraft_credentials(GenerationAction::GenerateVideo); + + if let Err(err) = event.send(&app) { + error!("Failed to emit event: {:?}", err); // Fail open. + } + + return Err(InternalVideoError::NeedsStorytellerCredentials); + }, + }; + + //let api_key = fal_creds_manager.get_key_required() + // .map_err(|err| { + // error!("EnqueueTextToImage FAL api key required: {:?}", err); + // InternalVideoError::NeedsFalApiKey + // })?; + + + info!("Calling Artcraft image to video..."); + + + let uuid_idempotency_token = generate_random_uuid(); + + let mut selected_model = None; + + let job_token = match request.model { + None => { + return Err(InternalVideoError::NoModelSpecified); + } + Some(EnqueueImageToVideoModel::Kling16) => { + info!("enqueue Kling 1.6"); + selected_model = Some(GenerationModel::Kling1_6); + let request = GenerateKling16ImageToVideoRequest { + uuid_idempotency_token, + media_file_token: request.image_media_token, + prompt: None, + }; + let result = generate_kling16_image_to_video( + &ApiHost::Storyteller, + Some(&creds), + request, + ).await; + match result { + Ok(enqueued) => { + info!("Successfully enqueued Artcraft Kling 1.6 video generation"); + enqueued.inference_job_token + } + Err(err) => { + error!("Failed to use Artcraft Kling 1.6 video generation: {:?}", err); + return Err(InternalVideoError::StorytellerError(err)); + } + } + } + }; + + info!("Successfully enqueued video generation"); + + let event = GenerationEnqueueSuccessEvent { + action: GenerationAction::GenerateVideo, + service: GenerationServiceProvider::Artcraft, + model: selected_model, + }; + + if let Err(err) = event.send(app) { + error!("Failed to emit event: {:?}", err); // Fail open. + } + + //if let Err(err) = fal_task_queue.insert(&enqueued) { + // error!("Failed to enqueue task: {:?}", err); + // return Err(InternalVideoError::AnyhowError(anyhow!("Failed to enqueue task: {:?}", err))); + //} + +// match result { +// Ok(enqueued) => { +// } +// Err(err) => { +// error!("Failed to enqueue image to 3d: {:?}", err); +// +// let event = GenerationEnqueueFailureEvent { +// action: GenerationAction::ImageTo3d, +// service: GenerationServiceProvider::Fal, +// model: None, +// reason: None, +// }; +// +// if let Err(err) = event.send(app) { +// error!("Failed to emit event: {:?}", err); // Fail open. +// } +// +// return Err(InternalVideoError::FalError(err)); +// } +// } + + Ok(()) +} diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/video/handle_video_fal.rs b/crates/desktop/artcraft/src/core/commands/enqueue/video/handle_video_fal.rs new file mode 100644 index 0000000000..eac01233f4 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/enqueue/video/handle_video_fal.rs @@ -0,0 +1,123 @@ +use crate::core::commands::enqueue::image::enqueue_text_to_image_command::{EnqueueTextToImageModel, EnqueueTextToImageRequest}; +use crate::core::commands::enqueue::video::enqueue_image_to_video_command::{EnqueueImageToVideoModel, EnqueueImageToVideoRequest}; +use crate::core::commands::enqueue::video::internal_video_error::InternalVideoError; +use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; +use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; +use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent; +use crate::core::events::generation_events::generation_enqueue_success_event::GenerationEnqueueSuccessEvent; +use crate::core::state::data_dir::app_data_root::AppDataRoot; +use crate::core::utils::download_media_file_to_temp_dir::download_media_file_to_temp_dir; +use crate::core::utils::save_base64_image_to_temp_dir::save_base64_image_to_temp_dir; +use crate::services::fal::state::fal_credential_manager::FalCredentialManager; +use crate::services::fal::state::fal_task_queue::FalTaskQueue; +use crate::services::sora::state::sora_credential_manager::SoraCredentialManager; +use crate::services::sora::state::sora_task_queue::SoraTaskQueue; +use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; +use anyhow::anyhow; +use fal_client::creds::fal_api_key::FalApiKey; +use fal_client::requests::queue::enqueue_hunyuan2_image_to_3d::{enqueue_hunyuan2_image_to_3d, Hunyuan2Args}; +use fal_client::requests::queue::image_gen::enqueue_flux_pro_ultra_text_to_image::{enqueue_flux_pro_ultra_text_to_image, FluxProUltraTextToImageArgs}; +use fal_client::requests::queue::image_gen::enqueue_recraft3_text_to_image::{enqueue_recraft3_text_to_image, Recraft3TextToImageArgs}; +use fal_client::requests::queue::video_gen::enqueue_kling_16_image_to_video::{enqueue_kling_16_image_to_video, Kling16Args, Kling16Duration}; +use log::{error, info, warn}; +use tauri::AppHandle; + +pub async fn handle_video_fal( + app: &AppHandle, + app_data_root: &AppDataRoot, + request: EnqueueImageToVideoRequest, + fal_creds_manager: &FalCredentialManager, + fal_task_queue: &FalTaskQueue, +) -> Result<(), InternalVideoError> { + + let api_key = match fal_creds_manager.get_key()? { + Some(key) => key, + None => { + error!("No FAL API key is set. Can't perform action."); + let event = + GenerationEnqueueFailureEvent::no_fal_api_key(GenerationAction::GenerateVideo); + + if let Err(err) = event.send(&app) { + error!("Failed to emit event: {:?}", err); // Fail open. + } + + return Err(InternalVideoError::NeedsFalApiKey); + }, + }; + + //let api_key = fal_creds_manager.get_key_required() + // .map_err(|err| { + // error!("EnqueueTextToImage FAL api key required: {:?}", err); + // InternalVideoError::NeedsFalApiKey + // })?; + + let mut temp_download; + + if let Some(media_token) = request.image_media_token { + temp_download = download_media_file_to_temp_dir(&app_data_root, &media_token).await?; + + } else { + return Err(InternalVideoError::AnyhowError(anyhow!("No image media token provided"))); + } + + info!("Calling FAL image to 3d ..."); + + let filename = temp_download.path().to_path_buf(); + + let mut selected_model = None; + + let result = match request.model { + None => { + return Err(InternalVideoError::NoModelSpecified); + } + Some(EnqueueImageToVideoModel::Kling16) => { + info!("enqueue Kling 1.6 image to video"); + selected_model = Some(GenerationModel::Kling1_6); + enqueue_kling_16_image_to_video(Kling16Args { + image_path: filename, + api_key: &api_key, + duration: Kling16Duration::Default, + prompt: "", + }).await + } + }; + + match result { + Ok(enqueued) => { + info!("Successfully enqueued Kling 1.6 image to video"); + + let event = GenerationEnqueueSuccessEvent { + action: GenerationAction::GenerateVideo, + service: GenerationServiceProvider::Fal, + model: selected_model, + }; + + if let Err(err) = event.send(app) { + error!("Failed to emit event: {:?}", err); // Fail open. + } + + if let Err(err) = fal_task_queue.insert(&enqueued) { + error!("Failed to enqueue task: {:?}", err); + return Err(InternalVideoError::AnyhowError(anyhow!("Failed to enqueue task: {:?}", err))); + } + } + Err(err) => { + error!("Failed to enqueue image to video: {:?}", err); + + let event = GenerationEnqueueFailureEvent { + action: GenerationAction::GenerateVideo, + service: GenerationServiceProvider::Fal, + model: selected_model, + reason: None, + }; + + if let Err(err) = event.send(app) { + error!("Failed to emit event: {:?}", err); // Fail open. + } + + return Err(InternalVideoError::FalError(err)); + } + } + + Ok(()) +} diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/video/internal_video_error.rs b/crates/desktop/artcraft/src/core/commands/enqueue/video/internal_video_error.rs index 6b0c19f18e..0973c58231 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/video/internal_video_error.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/video/internal_video_error.rs @@ -1,7 +1,7 @@ +use crate::core::artcraft_error::ArtcraftError; use base64::DecodeError; use errors::AnyhowError; use fal_client::error::fal_error_plus::FalErrorPlus; -use storyteller_client::error::api_error::ApiError; use storyteller_client::error::storyteller_error::StorytellerError; #[derive(Debug)] @@ -13,9 +13,9 @@ pub enum InternalVideoError { FalError(FalErrorPlus), AnyhowError(AnyhowError), StorytellerError(StorytellerError), - StorytellerApiError(ApiError), DecodeError(DecodeError), IoError(std::io::Error), + ArtcraftError(ArtcraftError), } impl From for InternalVideoError { @@ -30,9 +30,9 @@ impl From for InternalVideoError { } } -impl From for InternalVideoError { - fn from(value: ApiError) -> Self { - Self::StorytellerApiError(value) +impl From for InternalVideoError { + fn from(value: StorytellerError) -> Self { + Self::StorytellerError(value) } } @@ -47,3 +47,15 @@ impl From for InternalVideoError { Self::IoError(value) } } + +impl From for InternalVideoError { + fn from(value: ArtcraftError) -> Self { + match value { + ArtcraftError::AnyhowError(e) => Self::AnyhowError(e), + ArtcraftError::DecodeError(e) => Self::DecodeError(e), + ArtcraftError::IoError(e) => Self::IoError(e), + ArtcraftError::StorytellerError(e) => Self::StorytellerError(e), + _ => Self::ArtcraftError(value), + } + } +} diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/video/mod.rs b/crates/desktop/artcraft/src/core/commands/enqueue/video/mod.rs index aa6ee0107c..c61b63d3ca 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/video/mod.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/video/mod.rs @@ -1,2 +1,4 @@ pub mod enqueue_image_to_video_command; +pub mod handle_video_artcraft; +pub mod handle_video_fal; pub mod internal_video_error; \ No newline at end of file diff --git a/crates/lib/api_clients/storyteller_client/src/generate/mod.rs b/crates/lib/api_clients/storyteller_client/src/generate/mod.rs index 305645c9d4..28968fc39e 100644 --- a/crates/lib/api_clients/storyteller_client/src/generate/mod.rs +++ b/crates/lib/api_clients/storyteller_client/src/generate/mod.rs @@ -1 +1,2 @@ -pub mod object; \ No newline at end of file +pub mod object; +pub mod video; \ No newline at end of file diff --git a/crates/lib/api_clients/storyteller_client/src/generate/object/generate_hunyuan2_image_to_3d.rs b/crates/lib/api_clients/storyteller_client/src/generate/object/generate_hunyuan2_image_to_3d.rs index 789efa1ebb..48764f1ecc 100644 --- a/crates/lib/api_clients/storyteller_client/src/generate/object/generate_hunyuan2_image_to_3d.rs +++ b/crates/lib/api_clients/storyteller_client/src/generate/object/generate_hunyuan2_image_to_3d.rs @@ -9,7 +9,6 @@ use artcraft_api_defs::generate::object::generate_hunyuan_2_image_to_3d::{Genera use log::{debug, info}; use reqwest::Client; -/// Upload an image media file from a file. pub async fn generate_hunyuan2_image_to_3d( api_host: &ApiHost, maybe_creds: Option<&StorytellerCredentialSet>, diff --git a/crates/lib/api_clients/storyteller_client/src/generate/video/generate_kling16_image_to_video.rs b/crates/lib/api_clients/storyteller_client/src/generate/video/generate_kling16_image_to_video.rs new file mode 100644 index 0000000000..33b0c7bd08 --- /dev/null +++ b/crates/lib/api_clients/storyteller_client/src/generate/video/generate_kling16_image_to_video.rs @@ -0,0 +1,68 @@ +use crate::credentials::storyteller_credential_set::StorytellerCredentialSet; +use crate::error::api_error::ApiError; +use crate::error::client_error::ClientError; +use crate::error::storyteller_error::StorytellerError; +use crate::utils::api_host::ApiHost; +use crate::utils::constants::{APPLICATION_JSON, USER_AGENT}; +use crate::utils::filter_bad_response::filter_bad_response; +use artcraft_api_defs::generate::video::generate_kling_1_6_image_to_video::{GenerateKling16ImageToVideoRequest, GenerateKling16ImageToVideoResponse, GENERATE_KLING_1_6_IMAGE_TO_VIDEO_URL_PATH}; +use log::{debug, info}; +use reqwest::Client; + +pub async fn generate_kling16_image_to_video( + api_host: &ApiHost, + maybe_creds: Option<&StorytellerCredentialSet>, + request: GenerateKling16ImageToVideoRequest, +) -> Result { + + let url = get_route(api_host); + + info!("Requesting {:?}", &url); + + let client = Client::builder() + .gzip(true) + .build() + .map_err(|err| StorytellerError::Client(ClientError::from(err)))?; + + let mut request_builder = client.post(url) + .header("User-Agent", USER_AGENT) + .header("Accept", APPLICATION_JSON) + .header("Content-Type", APPLICATION_JSON); + + if let Some(creds) = maybe_creds { + if let Some(header) = &creds.maybe_as_cookie_header() { + request_builder = request_builder.header("Cookie", header); + } + } + + let request_body = serde_json::to_string(&request) + .map_err(|err| StorytellerError::Client(ClientError::from(err)))?; + + debug!("Request body: {:?}", request_body); + + let response = request_builder + .body(request_body) + .send() + .await + .map_err(|err| StorytellerError::Api(ApiError::from(err)))?; + + let response = filter_bad_response(response) + .await + .map_err(|err| StorytellerError::Api(ApiError::from(err)))?; + + let response_body = &response.text() + .await + .map_err(|err| StorytellerError::Api(ApiError::from(err)))?; + + debug!("Response body: {:?}", response_body); + + let response = serde_json::from_str(&response_body) + .map_err(|err| StorytellerError::Api(ApiError::from(err)))?; + + Ok(response) +} + +fn get_route(api_host: &ApiHost) -> String { + let api_hostname = api_host.to_api_hostname(); + format!("https://{}{}", api_hostname, GENERATE_KLING_1_6_IMAGE_TO_VIDEO_URL_PATH) +} diff --git a/crates/lib/api_clients/storyteller_client/src/generate/video/mod.rs b/crates/lib/api_clients/storyteller_client/src/generate/video/mod.rs new file mode 100644 index 0000000000..66ebbedd46 --- /dev/null +++ b/crates/lib/api_clients/storyteller_client/src/generate/video/mod.rs @@ -0,0 +1 @@ +pub mod generate_kling16_image_to_video; \ No newline at end of file diff --git a/crates/schema/public/api/artcraft_api_defs/src/generate/video/generate_kling_1_6_image_to_video.rs b/crates/schema/public/api/artcraft_api_defs/src/generate/video/generate_kling_1_6_image_to_video.rs index fc85085a03..75eb6193da 100644 --- a/crates/schema/public/api/artcraft_api_defs/src/generate/video/generate_kling_1_6_image_to_video.rs +++ b/crates/schema/public/api/artcraft_api_defs/src/generate/video/generate_kling_1_6_image_to_video.rs @@ -3,7 +3,7 @@ use tokens::tokens::generic_inference_jobs::InferenceJobToken; use tokens::tokens::media_files::MediaFileToken; use utoipa::ToSchema; -const GENERATE_KLING_1_6_IMAGE_TO_VIDEO_URL_PATH: &str = "/v1/generate/video/kling_16_image_to_video"; +pub const GENERATE_KLING_1_6_IMAGE_TO_VIDEO_URL_PATH: &str = "/v1/generate/video/kling_16_image_to_video"; #[derive(Serialize, Deserialize, ToSchema)] pub struct GenerateKling16ImageToVideoRequest {