mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-13 05:12:50 +00:00
WORKING!
This commit is contained in:
1 parent
292b831d16
commit
57e2f70707
11 files changed
+415
-104
No files matched your search
Generated
+1
@@ -3070,6 +3070,7 @@ dependencies = [
|
||||
"newrelic-telemetry",
|
||||
"once_cell",
|
||||
"r2d2_redis",
|
||||
"rand 0.8.5",
|
||||
"redis_common",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
||||
@@ -21,6 +21,10 @@ pub enum IdCategory {
|
||||
#[serde(rename = "lipsync_animation")]
|
||||
LipsyncAnimationResult,
|
||||
|
||||
// Results from rerender a video
|
||||
#[serde(rename = "rerender")]
|
||||
RerenderResult,
|
||||
|
||||
/// Results from tacotron2
|
||||
/// Applies for RVC and SVC
|
||||
#[serde(rename = "tts_result")]
|
||||
@@ -62,6 +66,7 @@ impl IdCategory {
|
||||
Self::ZeroShotVoiceDataset => "zs_dataset",
|
||||
Self::ZeroShotVoiceEmbedding => "zs_voice",
|
||||
Self::ZeroShotTtsResult => "zs_tts_result",
|
||||
Self::RerenderResult => "rerender",
|
||||
Self::ModelWeights => "model_weights",
|
||||
}
|
||||
}
|
||||
@@ -75,6 +80,7 @@ impl IdCategory {
|
||||
"zs_dataset" => Ok(Self::ZeroShotVoiceDataset),
|
||||
"zs_voice" => Ok(Self::ZeroShotVoiceEmbedding),
|
||||
"zs_tts_result" => Ok(Self::ZeroShotTtsResult),
|
||||
"rerender" => Ok(Self::RerenderResult),
|
||||
"model_weights" => Ok(Self::ModelWeights),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ pub enum MediaFileOriginModelType {
|
||||
|
||||
#[serde(rename = "vall_e_x")]
|
||||
VallEX,
|
||||
|
||||
#[serde(rename = "rerender")]
|
||||
Rerender,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
@@ -45,7 +48,8 @@ impl MediaFileOriginModelType {
|
||||
Self::SadTalker => "sad_talker",
|
||||
Self::SoVitsSvc => "so_vits_svc",
|
||||
Self::Tacotron2 => "tacotron2",
|
||||
Self::VallEX => "vall_e_x"
|
||||
Self::VallEX => "vall_e_x",
|
||||
Self::Rerender => "rerender",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +60,7 @@ impl MediaFileOriginModelType {
|
||||
"so_vits_svc" => Ok(Self::SoVitsSvc),
|
||||
"tacotron2" => Ok(Self::Tacotron2),
|
||||
"vall_e_x" => Ok(Self::VallEX),
|
||||
"rerender" => Ok(Self::Rerender),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ pub enum MediaFileOriginProductCategory {
|
||||
/// Media files created by (or uploaded for) the Zero Shot voice product.
|
||||
#[serde(rename = "zs_voice")]
|
||||
ZeroShotVoice,
|
||||
|
||||
// Media files for Rerender
|
||||
#[serde(rename = "rerender")]
|
||||
Rerender,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
@@ -54,6 +58,7 @@ impl MediaFileOriginProductCategory {
|
||||
Self::Unknown => "unknown",
|
||||
Self::VoiceConversion => "voice_conversion",
|
||||
Self::ZeroShotVoice => "zs_voice",
|
||||
Self::Rerender => "rerender",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +69,7 @@ impl MediaFileOriginProductCategory {
|
||||
"unknown" => Ok(Self::Unknown),
|
||||
"voice_conversion" => Ok(Self::VoiceConversion),
|
||||
"zs_voice" => Ok(Self::ZeroShotVoice),
|
||||
"rerender" => Ok(Self::Rerender),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
use anyhow::anyhow;
|
||||
use sqlx;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use enums::by_table::generic_synthetic_ids::id_category::IdCategory;
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob;
|
||||
use crate::queries::generic_synthetic_ids::transactional_increment_generic_synthetic_id::transactional_increment_generic_synthetic_id;
|
||||
|
||||
pub struct InsertArgs<'a> {
|
||||
pub pool: &'a MySqlPool,
|
||||
pub job: &'a AvailableInferenceJob,
|
||||
|
||||
pub maybe_mime_type: Option<&'a str>,
|
||||
pub file_size_bytes: u64,
|
||||
pub sha256_checksum: &'a str,
|
||||
// TODO: Media duration.
|
||||
//pub duration_millis: u64,
|
||||
|
||||
pub public_bucket_directory_hash: &'a str,
|
||||
pub maybe_public_bucket_prefix: Option<&'a str>,
|
||||
pub maybe_public_bucket_extension: Option<&'a str>,
|
||||
|
||||
pub is_on_prem: bool,
|
||||
pub worker_hostname: &'a str,
|
||||
pub worker_cluster: &'a str,
|
||||
}
|
||||
|
||||
pub async fn insert_media_file_from_rerender(
|
||||
args: InsertArgs<'_>
|
||||
) -> AnyhowResult<(MediaFileToken, u64)>
|
||||
{
|
||||
let result_token = MediaFileToken::generate();
|
||||
|
||||
let mut maybe_creator_file_synthetic_id : Option<u64> = None;
|
||||
let mut maybe_creator_category_synthetic_id : Option<u64> = None;
|
||||
|
||||
let mut transaction = args.pool.begin().await?;
|
||||
|
||||
if let Some(creator_user_token) = args.job.maybe_creator_user_token.as_deref() {
|
||||
let user_token = UserToken::new_from_str(creator_user_token);
|
||||
|
||||
let next_media_file_id = transactional_increment_generic_synthetic_id(
|
||||
&user_token,
|
||||
IdCategory::MediaFile,
|
||||
&mut transaction
|
||||
).await?;
|
||||
|
||||
let next_rerender_id = transactional_increment_generic_synthetic_id(
|
||||
&user_token,
|
||||
IdCategory::RerenderResult,
|
||||
&mut transaction
|
||||
).await?;
|
||||
|
||||
maybe_creator_file_synthetic_id = Some(next_media_file_id);
|
||||
maybe_creator_category_synthetic_id = Some(next_rerender_id);
|
||||
}
|
||||
|
||||
let creator_ip_address = args.job.creator_ip_address.as_str();
|
||||
let creator_set_visibility = args.job.creator_set_visibility.clone();
|
||||
|
||||
const ORIGIN_CATEGORY : MediaFileOriginCategory = MediaFileOriginCategory::Inference;
|
||||
const ORIGIN_PRODUCT_CATEGORY : MediaFileOriginProductCategory = MediaFileOriginProductCategory::Rerender;
|
||||
const ORIGIN_MODEL_TYPE : MediaFileOriginModelType = MediaFileOriginModelType::Rerender;
|
||||
const MEDIA_TYPE : MediaFileType = MediaFileType::Video;
|
||||
|
||||
let record_id = {
|
||||
let query_result = sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO media_files
|
||||
SET
|
||||
token = ?,
|
||||
|
||||
origin_category = ?,
|
||||
origin_product_category = ?,
|
||||
maybe_origin_model_type = ?,
|
||||
|
||||
media_type = ?,
|
||||
maybe_mime_type = ?,
|
||||
file_size_bytes = ?,
|
||||
|
||||
checksum_sha2 = ?,
|
||||
|
||||
public_bucket_directory_hash = ?,
|
||||
maybe_public_bucket_prefix = ?,
|
||||
maybe_public_bucket_extension = ?,
|
||||
|
||||
maybe_creator_user_token = ?,
|
||||
maybe_creator_anonymous_visitor_token = ?,
|
||||
creator_ip_address = ?,
|
||||
|
||||
creator_set_visibility = ?,
|
||||
|
||||
maybe_creator_file_synthetic_id = ?,
|
||||
maybe_creator_category_synthetic_id = ?,
|
||||
|
||||
is_generated_on_prem = ?,
|
||||
generated_by_worker = ?,
|
||||
generated_by_cluster = ?
|
||||
|
||||
"#,
|
||||
result_token.as_str(),
|
||||
|
||||
ORIGIN_CATEGORY.to_str(),
|
||||
ORIGIN_PRODUCT_CATEGORY.to_str(),
|
||||
ORIGIN_MODEL_TYPE.to_str(),
|
||||
|
||||
MEDIA_TYPE.to_str(),
|
||||
args.maybe_mime_type,
|
||||
args.file_size_bytes,
|
||||
|
||||
args.sha256_checksum,
|
||||
|
||||
args.public_bucket_directory_hash,
|
||||
args.maybe_public_bucket_prefix,
|
||||
args.maybe_public_bucket_extension,
|
||||
|
||||
args.job.maybe_creator_user_token,
|
||||
args.job.maybe_creator_anonymous_visitor_token,
|
||||
args.job.creator_ip_address,
|
||||
|
||||
args.job.creator_set_visibility.to_str(),
|
||||
|
||||
maybe_creator_file_synthetic_id,
|
||||
maybe_creator_category_synthetic_id,
|
||||
|
||||
args.is_on_prem,
|
||||
args.worker_hostname,
|
||||
args.worker_cluster
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await;
|
||||
|
||||
let record_id = match query_result {
|
||||
Ok(res) => {
|
||||
res.last_insert_id()
|
||||
},
|
||||
Err(err) => {
|
||||
// TODO: handle better
|
||||
//transaction.rollback().await?;
|
||||
return Err(anyhow!("Mysql error: {:?}", err));
|
||||
}
|
||||
};
|
||||
|
||||
record_id
|
||||
};
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
Ok((result_token, record_id))
|
||||
}
|
||||
@@ -8,3 +8,4 @@ pub mod insert_media_file_from_zero_shot_tts;
|
||||
pub mod list_media_files;
|
||||
pub mod list_media_files_for_user;
|
||||
pub mod update_media_file;
|
||||
pub mod insert_media_file_from_rerender;
|
||||
@@ -81,3 +81,4 @@ async-trait = "0.1.58"
|
||||
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
|
||||
time = "0.3.30"
|
||||
mime = "0.3.17"
|
||||
rand = { version = "0.8.5", features = [] }
|
||||
@@ -101,7 +101,7 @@ VITS_INFERENCE_MAYBE_DOCKER_IMAGE_SHA="03017896367b"
|
||||
# #
|
||||
# ================================= #
|
||||
RERENDER_INFERENCE_ROOT_DIRECTORY="/home/salt/clone/storyteller-ml/animation/rerender-lora/"
|
||||
RERENDER_INFERENCE_EXECUTABLE_OR_COMMAND="conda activate rerender && python rerender.py"
|
||||
RERENDER_INFERENCE_EXECUTABLE_OR_COMMAND="venv/bin/python rerender.py"
|
||||
|
||||
# ================================= #
|
||||
# #
|
||||
|
||||
+45
-31
@@ -1,43 +1,56 @@
|
||||
use anyhow::anyhow;
|
||||
use log::{error, info};
|
||||
use log::info;
|
||||
|
||||
use mysql_queries::payloads::generic_inference_args::generic_inference_args::PolymorphicInferenceArgs::Rr;
|
||||
use mysql_queries::payloads::generic_inference_args::videofilter_payload::VideofilterVideoSource;
|
||||
use mysql_queries::queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob;
|
||||
use mysql_queries::queries::media_uploads::get_media_upload_for_inference::get_media_upload_for_inference;
|
||||
use tokens::tokens::media_uploads::MediaUploadToken;
|
||||
use mysql_queries::queries::media_files::get_media_file::get_media_file;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::job::job_loop::job_success_result::JobSuccessResult;
|
||||
use crate::job::job_loop::process_single_job_error::ProcessSingleJobError;
|
||||
use crate::job::job_types::videofilter::rerender_a_video;
|
||||
use crate::job::job_types::videofilter::rerender_a_video::process_job::RerenderProcessJobArgs;
|
||||
use crate::job_dependencies::JobDependencies;
|
||||
|
||||
pub async fn process_single_rr_job(job_dependencies: &JobDependencies, job: &AvailableInferenceJob) -> Result<JobSuccessResult, ProcessSingleJobError> {
|
||||
let maybe_media_upload_token = job.maybe_input_source_token
|
||||
.as_deref()
|
||||
.map(|token| MediaUploadToken::new_from_str(token));
|
||||
// let maybe_inference_args = job.maybe_inference_args.as_ref().ok_or(ProcessSingleJobError::Other(anyhow!("Inference args not found")))?;
|
||||
//
|
||||
// let maybe_rerender_args = maybe_inference_args.args.as_ref().map(|args| match args {
|
||||
// Rr(args) => Some(args),
|
||||
// _ => None,
|
||||
// }).flatten();
|
||||
//
|
||||
// let rerender_args = match maybe_rerender_args {
|
||||
// None => return Err(ProcessSingleJobError::Other(anyhow!("Rerender args not found"))),
|
||||
// Some(args) => args,
|
||||
// };
|
||||
//
|
||||
// let videofilter_source = rerender_args.maybe_video_source.as_ref().ok_or(ProcessSingleJobError::Other(anyhow!("Video source not found")))?;
|
||||
//
|
||||
// let media_file_token = match videofilter_source {
|
||||
// VideofilterVideoSource::F(token) => token,
|
||||
// _ => return Err(ProcessSingleJobError::Other(anyhow!("Video source not found"))),
|
||||
// };
|
||||
//
|
||||
// let media_file_token = MediaFileToken::new_from_str(media_file_token);
|
||||
//
|
||||
// info!("media_file_token: {:?}", media_file_token);
|
||||
//
|
||||
//
|
||||
// let media_file = match media_file_result {
|
||||
// Ok(Some(result)) => result,
|
||||
// Ok(None) => {
|
||||
// return Err(ProcessSingleJobError::Other(anyhow!("Media file not found")));
|
||||
// }
|
||||
// Err(e) => {
|
||||
// return Err(ProcessSingleJobError::Other(anyhow!("Media file not found")));
|
||||
// }
|
||||
// };
|
||||
|
||||
let media_upload_token = match maybe_media_upload_token {
|
||||
None => return Err(ProcessSingleJobError::Other(anyhow!("no associated media upload for vc job: {:?}", job.inference_job_token))),
|
||||
Some(token) => token,
|
||||
};
|
||||
|
||||
let maybe_media_upload_result =
|
||||
get_media_upload_for_inference(&media_upload_token, &job_dependencies.db.mysql_pool).await;
|
||||
|
||||
let media_upload = match maybe_media_upload_result {
|
||||
Ok(Some(media_upload)) => media_upload,
|
||||
Ok(None) => {
|
||||
error!("no media upload record found for token: {:?}", media_upload_token);
|
||||
return Err(ProcessSingleJobError::Other(anyhow!("no media upload record found for token: {:?}", media_upload_token)));
|
||||
},
|
||||
Err(err) => {
|
||||
error!("error fetching media upload record from db: {:?}", err);
|
||||
return Err(ProcessSingleJobError::Other(err));
|
||||
},
|
||||
};
|
||||
|
||||
info!("Source media upload file size (bytes): {}", &media_upload.original_file_size_bytes);
|
||||
info!("Source media upload duration (millis): {}", &media_upload.original_duration_millis);
|
||||
info!("Source media upload duration (seconds): {}", (media_upload.original_duration_millis as f32 / 1000.0));
|
||||
// info!("Source media upload file size (bytes): {}", &media_upload.original_file_size_bytes);
|
||||
// info!("Source media upload duration (millis): {}", &media_upload.original_duration_millis);
|
||||
// info!("Source media upload duration (seconds): {}", (media_upload.original_duration_millis as f32 / 1000.0));
|
||||
|
||||
|
||||
|
||||
@@ -54,9 +67,10 @@ pub async fn process_single_rr_job(job_dependencies: &JobDependencies, job: &Ava
|
||||
// };
|
||||
|
||||
let job_success_result = rerender_a_video::process_job::process_job(
|
||||
rerender_a_video::process_job::RerenderProcessJobArgs {
|
||||
RerenderProcessJobArgs {
|
||||
job_dependencies,
|
||||
job
|
||||
job,
|
||||
// media_file
|
||||
}
|
||||
).await?;
|
||||
|
||||
|
||||
+29
-3
@@ -4,13 +4,16 @@ use anyhow::anyhow;
|
||||
use log::{error, warn};
|
||||
use sqlx::MySqlPool;
|
||||
use tempdir::TempDir;
|
||||
use buckets::public::media_files::original_file::MediaFileBucketPath;
|
||||
|
||||
use buckets::public::media_uploads::original_file::MediaUploadOriginalFilePath;
|
||||
use cloud_storage::bucket_client::BucketClient;
|
||||
use jobs_common::job_progress_reporter::job_progress_reporter::JobProgressReporter;
|
||||
use mysql_queries::payloads::generic_inference_args::videofilter_payload::VideofilterVideoSource;
|
||||
use mysql_queries::queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob;
|
||||
use mysql_queries::queries::media_files::get_media_file::get_media_file;
|
||||
use mysql_queries::queries::media_uploads::get_media_upload_for_inference::get_media_upload_for_inference;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::media_uploads::MediaUploadToken;
|
||||
|
||||
use crate::job::job_loop::process_single_job_error::ProcessSingleJobError;
|
||||
@@ -34,9 +37,32 @@ pub async fn download_video_file(
|
||||
let bucket_object_path;
|
||||
|
||||
match video_source {
|
||||
VideofilterVideoSource::F(_media_file_token) => {
|
||||
// TODO(bt, 2023-09-08): Implement
|
||||
return Err(ProcessSingleJobError::NotYetImplemented)
|
||||
VideofilterVideoSource::F(media_file_token) => {
|
||||
let media_file_token = MediaFileToken::new_from_str(media_file_token);
|
||||
let media_file_result = get_media_file(
|
||||
&media_file_token,
|
||||
false,
|
||||
mysql_pool
|
||||
).await;
|
||||
match media_file_result {
|
||||
Ok(Some(result)) => {
|
||||
let media_file_bucket_path =
|
||||
MediaFileBucketPath::from_object_hash(
|
||||
&result.public_bucket_directory_hash,
|
||||
result.maybe_public_bucket_prefix.as_deref(),
|
||||
result.maybe_public_bucket_extension.as_deref(),
|
||||
);
|
||||
bucket_object_path = media_file_bucket_path.to_full_object_pathbuf();
|
||||
}
|
||||
Ok(None) => {
|
||||
return Err(ProcessSingleJobError::from_anyhow_error(
|
||||
anyhow!("could not find media file: {:?}", media_file_token)))
|
||||
}
|
||||
Err(e) => {
|
||||
error!("could not query media file: {:?}", e);
|
||||
return Err(ProcessSingleJobError::from_anyhow_error(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
VideofilterVideoSource::U(media_upload_token) => {
|
||||
let media_upload_token = MediaUploadToken::new_from_str(media_upload_token);
|
||||
|
||||
+161
-68
@@ -1,8 +1,9 @@
|
||||
use std::fs::read_to_string;
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use anyhow::{anyhow, Error};
|
||||
use log::{error, info, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use buckets::public::media_files::original_file::MediaFileBucketPath;
|
||||
use enums::by_table::generic_inference_jobs::inference_result_type::InferenceResultType;
|
||||
@@ -12,19 +13,16 @@ use filesys::safe_delete_temp_directory::safe_delete_temp_directory;
|
||||
use filesys::safe_delete_temp_file::safe_delete_temp_file;
|
||||
use hashing::sha256::sha256_hash_file::sha256_hash_file;
|
||||
use mimetypes::mimetype_for_file::get_mimetype_for_file;
|
||||
use mysql_queries::payloads::generic_inference_args::generic_inference_args::PolymorphicInferenceArgs::Rr;
|
||||
use mysql_queries::queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob;
|
||||
use mysql_queries::queries::media_files::insert_media_file_from_face_animation::{insert_media_file_from_face_animation, InsertArgs};
|
||||
use tokens::tokens::users::UserToken;
|
||||
use mysql_queries::queries::media_files::insert_media_file_from_rerender::{insert_media_file_from_rerender, InsertArgs};
|
||||
|
||||
use crate::job::job_loop::job_success_result::{JobSuccessResult, ResultEntity};
|
||||
use crate::job::job_loop::process_single_job_error::ProcessSingleJobError;
|
||||
use crate::job::job_types::lipsync::sad_talker::categorize_error::categorize_error;
|
||||
// use crate::job::job_types::lipsync::sad_talker::categorize_error::categorize_error;
|
||||
use crate::job::job_types::videofilter::rerender_a_video::download_video_file::download_video_file;
|
||||
use crate::job::job_types::videofilter::rerender_a_video::rerender_inference_command::InferenceArgs;
|
||||
use crate::job::job_types::videofilter::rerender_a_video::validate_job::validate_job;
|
||||
use crate::job_dependencies::JobDependencies;
|
||||
use crate::util::common_commands::ffmpeg_logo_watermark_command;
|
||||
|
||||
/// The maximum that either width or height can be
|
||||
const MAX_DIMENSION : u32 = 1500;
|
||||
@@ -35,6 +33,7 @@ const BUCKET_FILE_EXTENSION: &str = ".mp4";
|
||||
pub struct RerenderProcessJobArgs<'a> {
|
||||
pub job_dependencies: &'a JobDependencies,
|
||||
pub job: &'a AvailableInferenceJob,
|
||||
// pub media_file: MediaFile,
|
||||
}
|
||||
|
||||
pub async fn process_job(args: RerenderProcessJobArgs<'_>) -> Result<JobSuccessResult, ProcessSingleJobError> {
|
||||
@@ -88,43 +87,8 @@ pub async fn process_job(args: RerenderProcessJobArgs<'_>) -> Result<JobSuccessR
|
||||
// ==================== TRANSCODE MEDIA (IF NECESSARY) ==================== //
|
||||
|
||||
let mut usable_video_path = video_path.filesystem_path.clone();
|
||||
//
|
||||
// if let Some((width, height)) = job_args.width.zip(job_args.height) {
|
||||
// info!("Requested image resize to {}x{} ...", width, height);
|
||||
//
|
||||
// let mut width = width.min(MAX_DIMENSION);
|
||||
// let mut height = height.min(MAX_DIMENSION);
|
||||
//
|
||||
// if job_args.enhancer.is_some() {
|
||||
// // The enhancer will double the size of the image, so we'll downsize the image by half
|
||||
// // if it's enabled.
|
||||
// width = width / 2;
|
||||
// height = height / 2;
|
||||
// info!("Enhancer will be doubling image size, so cutting frame size to {}x{}.", width, height)
|
||||
// }
|
||||
//
|
||||
// info!("Resizing image to {}x{} ...", width, height);
|
||||
//
|
||||
// let result = resize_image(
|
||||
// &image_path.filesystem_path,
|
||||
// &work_temp_dir,
|
||||
// width,
|
||||
// height
|
||||
// );
|
||||
//
|
||||
// match result {
|
||||
// Err(err) => {
|
||||
// error!("Image resize failure: {:?}", err);
|
||||
// safe_delete_temp_file(&audio_path.filesystem_path);
|
||||
// safe_delete_temp_file(&image_path.filesystem_path);
|
||||
// safe_delete_temp_directory(&work_temp_dir);
|
||||
// return Err(ProcessSingleJobError::Other(anyhow!("image resize failure: {:?}", err)));
|
||||
// }
|
||||
// Ok(resized_image_path) => {
|
||||
// usable_image_path = resized_image_path;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
//TODO: re encode with ffmpeg
|
||||
|
||||
info!("Used video file: {:?}", usable_video_path);
|
||||
|
||||
@@ -148,15 +112,43 @@ pub async fn process_job(args: RerenderProcessJobArgs<'_>) -> Result<JobSuccessR
|
||||
.map(|args| args.args.as_ref())
|
||||
.flatten();
|
||||
|
||||
// ==================== WRITE CONFIG TO DISK ==================== //
|
||||
let poly_args = match maybe_args {
|
||||
None => return Err(ProcessSingleJobError::Other(anyhow!("Rerender args not found"))),
|
||||
Some(args) => args,
|
||||
};
|
||||
|
||||
let rr_args = match poly_args {
|
||||
Rr(args) => args,
|
||||
_ => return Err(ProcessSingleJobError::Other(anyhow!("Rerender args not found"))),
|
||||
};
|
||||
|
||||
let sd_model = rr_args.maybe_sd_model.as_ref().ok_or(ProcessSingleJobError::Other(anyhow!("SD model not found")))?;
|
||||
|
||||
// ============== CREATE AND WRITE CONFIG TO DISK ============== //
|
||||
|
||||
// create seed if not exists
|
||||
// let seed = rr_args.seed.unwrap_or_else(|| rand::random::<i64>());
|
||||
let seed = rand::random::<i32>();
|
||||
|
||||
let config = RerenderConfig {
|
||||
input: Some(usable_video_path.to_str().unwrap().to_string()),
|
||||
output: Some(output_video_fs_path.to_str().unwrap().to_string()),
|
||||
work_dir: Some(work_temp_dir.path().to_str().unwrap().to_string()),
|
||||
sd_model: Some(sd_model.to_string()),
|
||||
prompt: Some("1girl, anime style, red hair".to_string()),
|
||||
a_prompt: Some("1girl, 3d, realistic, read hair".to_string()),
|
||||
n_prompt: Some("Bad quality, low quality, photo, artifacts, watermark, signature, faceless".to_string()),
|
||||
seed: Some(seed as i64),
|
||||
..RerenderConfig::new()
|
||||
};
|
||||
|
||||
config.validate().map_err(|e| ProcessSingleJobError::Other(anyhow!(e)))?;
|
||||
|
||||
let config_path = work_temp_dir.path().join("config.json");
|
||||
|
||||
// TODO
|
||||
let config_json = serde_json::to_string(&config).unwrap();
|
||||
std::fs::write(&config_path, config_json).unwrap();
|
||||
|
||||
// ==================== RUN INFERENCE SCRIPT ==================== //
|
||||
|
||||
let workdir = work_temp_dir.path().to_path_buf();
|
||||
let stderr_output_file = work_temp_dir.path().join("stderr.txt");
|
||||
let inference_start_time = Instant::now();
|
||||
|
||||
@@ -174,7 +166,7 @@ pub async fn process_job(args: RerenderProcessJobArgs<'_>) -> Result<JobSuccessR
|
||||
if !command_exit_status.is_success() {
|
||||
error!("Inference failed: {:?}", command_exit_status);
|
||||
|
||||
let mut error = ProcessSingleJobError::Other(anyhow!("CommandExitStatus: {:?}", command_exit_status));
|
||||
let error = ProcessSingleJobError::Other(anyhow!("CommandExitStatus: {:?}", command_exit_status));
|
||||
|
||||
if let Ok(contents) = read_to_string(&stderr_output_file) {
|
||||
warn!("Captured stderr output: {}", contents);
|
||||
@@ -205,7 +197,7 @@ pub async fn process_job(args: RerenderProcessJobArgs<'_>) -> Result<JobSuccessR
|
||||
|
||||
// ==================== OPTIONAL WATERMARK ==================== //
|
||||
|
||||
let mut finished_file = output_video_fs_path.clone();
|
||||
let finished_file = output_video_fs_path.clone();
|
||||
|
||||
// ==================== CHECK ALL FILES EXIST AND GET METADATA ==================== //
|
||||
|
||||
@@ -231,7 +223,7 @@ pub async fn process_job(args: RerenderProcessJobArgs<'_>) -> Result<JobSuccessR
|
||||
ProcessSingleJobError::Other(anyhow!("Error hashing file: {:?}", err))
|
||||
})?;
|
||||
|
||||
// ==================== UPLOAD AUDIO TO BUCKET ==================== //
|
||||
// ==================== UPLOAD VIDEO TO BUCKET ==================== //
|
||||
|
||||
job_progress_reporter.log_status("uploading result")
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
@@ -266,9 +258,9 @@ pub async fn process_job(args: RerenderProcessJobArgs<'_>) -> Result<JobSuccessR
|
||||
|
||||
// ==================== SAVE RECORDS ==================== //
|
||||
|
||||
info!("Saving SadTalker result (media_files table record) ...");
|
||||
info!("Saving Rerender result (media_files table record) ...");
|
||||
|
||||
let (media_file_token, id) = insert_media_file_from_face_animation(InsertArgs {
|
||||
let (media_file_token, id) = insert_media_file_from_rerender(InsertArgs {
|
||||
pool: &args.job_dependencies.db.mysql_pool,
|
||||
job: &job,
|
||||
maybe_mime_type: Some(&mimetype),
|
||||
@@ -284,21 +276,7 @@ pub async fn process_job(args: RerenderProcessJobArgs<'_>) -> Result<JobSuccessR
|
||||
.await
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
|
||||
info!("SadTalker Done.");
|
||||
|
||||
// TODO: Update upstream to be strongly typed
|
||||
let maybe_user_token = job.maybe_creator_user_token.as_deref()
|
||||
.map(|token| UserToken::new_from_str(token));
|
||||
|
||||
args.job_dependencies.clients.firehose_publisher.lipsync_animation_finished(
|
||||
maybe_user_token.as_ref(),
|
||||
&job.inference_job_token,
|
||||
media_file_token.as_str())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("error publishing event: {:?}", e);
|
||||
ProcessSingleJobError::Other(anyhow!("error publishing event"))
|
||||
})?;
|
||||
info!("Rerender Done.");
|
||||
|
||||
job_progress_reporter.log_status("done")
|
||||
.map_err(|e| ProcessSingleJobError::Other(e))?;
|
||||
@@ -314,3 +292,118 @@ pub async fn process_job(args: RerenderProcessJobArgs<'_>) -> Result<JobSuccessR
|
||||
inference_duration,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct RerenderConfig {
|
||||
#[serde(rename = "input")]
|
||||
pub input: Option<String>,
|
||||
|
||||
#[serde(rename = "output")]
|
||||
pub output: Option<String>,
|
||||
|
||||
#[serde(rename = "work_dir")]
|
||||
pub work_dir: Option<String>,
|
||||
|
||||
#[serde(rename = "key_subdir")]
|
||||
pub key_subdir: Option<String>,
|
||||
|
||||
#[serde(rename = "sd_model")]
|
||||
pub sd_model: Option<String>,
|
||||
|
||||
#[serde(rename = "lora_path")]
|
||||
pub lora_path: Option<String>,
|
||||
|
||||
#[serde(rename = "interval")]
|
||||
pub interval: Option<i32>,
|
||||
|
||||
#[serde(rename = "prompt")]
|
||||
pub prompt: Option<String>,
|
||||
|
||||
#[serde(rename = "a_prompt")]
|
||||
pub a_prompt: Option<String>,
|
||||
|
||||
#[serde(rename = "n_prompt")]
|
||||
pub n_prompt: Option<String>,
|
||||
|
||||
#[serde(rename = "x0_strength")]
|
||||
pub x0_strength: Option<f32>,
|
||||
|
||||
#[serde(rename = "control_type")]
|
||||
pub control_type: Option<String>,
|
||||
|
||||
#[serde(rename = "canny_low")]
|
||||
pub canny_low: Option<i32>,
|
||||
|
||||
#[serde(rename = "canny_high")]
|
||||
pub canny_high: Option<i32>,
|
||||
|
||||
#[serde(rename = "control_strength")]
|
||||
pub control_strength: Option<f32>,
|
||||
|
||||
#[serde(rename = "seed")]
|
||||
pub seed: Option<i64>,
|
||||
|
||||
#[serde(rename = "warp_period")]
|
||||
pub warp_period: Option<Vec<f32>>,
|
||||
|
||||
#[serde(rename = "ada_period")]
|
||||
pub ada_period: Option<Vec<f32>>,
|
||||
}
|
||||
|
||||
impl RerenderConfig {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
input: None,
|
||||
output: None,
|
||||
work_dir: None,
|
||||
key_subdir: Some("keys".to_string()),
|
||||
sd_model: None,
|
||||
lora_path: None,
|
||||
interval: Some(8),
|
||||
prompt: None,
|
||||
a_prompt: None,
|
||||
n_prompt: None,
|
||||
x0_strength: Some(0.95),
|
||||
control_type: Some("canny".to_string()),
|
||||
canny_low: Some(50),
|
||||
canny_high: Some(100),
|
||||
control_strength: Some(0.7),
|
||||
seed: None,
|
||||
warp_period: Some(vec![0., 0.1]),
|
||||
ada_period: Some(vec![0.8, 1.]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> Result<(), Error> {
|
||||
let fields = &[
|
||||
(self.input.is_some(), "input"),
|
||||
(self.output.is_some(), "output"),
|
||||
(self.work_dir.is_some(), "work_dir"),
|
||||
(self.key_subdir.is_some(), "key_subdir"),
|
||||
(self.sd_model.is_some(), "sd_model"),
|
||||
(self.interval.is_some(), "interval"),
|
||||
(self.prompt.is_some(), "prompt"),
|
||||
(self.a_prompt.is_some(), "a_prompt"),
|
||||
(self.n_prompt.is_some(), "n_prompt"),
|
||||
(self.x0_strength.is_some(), "x0_strength"),
|
||||
(self.control_type.is_some(), "control_type"),
|
||||
(self.canny_low.is_some(), "canny_low"),
|
||||
(self.canny_high.is_some(), "canny_high"),
|
||||
(self.control_strength.is_some(), "control_strength"),
|
||||
(self.seed.is_some(), "seed"),
|
||||
(self.warp_period.is_some(), "warp_period"),
|
||||
(self.ada_period.is_some(), "ada_period"),
|
||||
];
|
||||
|
||||
let missing_fields: Vec<&str> = fields
|
||||
.iter()
|
||||
.filter_map(|(is_some, field_name)| if *is_some { None } else { Some(*field_name) })
|
||||
.collect();
|
||||
|
||||
if missing_fields.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!("Missing fields: {}", missing_fields.join(", ")))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user