Merge pull request #63 from storytold/bt-twitter-dimensions

SadTalker: Final API shape. Twitter dimensions, non-default options are opt-in, premium watermark
This commit is contained in:
Brandon Thomas authored and GitHub committed 2023-10-04 00:31:00 -04:00
commit d0e3e0f6bb
13 files changed
+313 -98

No files matched your search

Generated
+2
View File
@@ -2941,6 +2941,7 @@ dependencies = [
"hashing",
"hostname",
"hyper 0.14.24",
"images",
"jobs_common",
"lfu",
"log",
@@ -2959,6 +2960,7 @@ dependencies = [
"subprocess",
"subprocess_common",
"tempdir",
"testing",
"tokens",
"tokio 1.25.0",
"tts_common",
+4
View File
@@ -19,4 +19,8 @@
#![allow(dead_code)]
#![allow(non_snake_case)]
pub mod resize_image_file_preserving_aspect;
pub mod resize_preserving_aspect;
// Re-export image to everyone importing this library
pub use image;
@@ -0,0 +1,43 @@
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use image::DynamicImage;
use image::io::Reader;
use errors::AnyhowResult;
use crate::resize_preserving_aspect::resize_preserving_aspect;
pub fn resize_image_file_preserving_aspect<P: AsRef<Path>>(
source_image_file: P,
new_width: u32,
new_height: u32,
exact_fit: bool,
) -> AnyhowResult<DynamicImage> {
let file = File::open(source_image_file)?;
let reader = BufReader::new(file);
let reader = Reader::new(reader)
.with_guessed_format()?;
let img = reader.decode()?;
Ok(resize_preserving_aspect(&img, new_width, new_height, exact_fit))
}
#[cfg(test)]
mod tests {
use testing::test_file_path::test_file_path;
use crate::resize_image_file_preserving_aspect::resize_image_file_preserving_aspect;
#[test]
fn test_happy_path() {
// NB: The other function does most of the testing
let path = test_file_path("test_data/image/mochi.jpg").unwrap();
let image = resize_image_file_preserving_aspect(path, 100, 100, true).unwrap();
assert_eq!(image.width(), 100);
assert_eq!(image.height(), 100);
}
}
@@ -1,8 +1,21 @@
use image::DynamicImage;
use image::imageops::FilterType;
pub fn resize_preserving_aspect(source_image: &DynamicImage, new_width: u32, new_height: u32) -> DynamicImage {
source_image.resize(new_width, new_height, FilterType::Lanczos3)
/// Resize an image preserving the exact aspect ratio of the source image.
///
/// If `exact_fit` is true, the image will be resized to fit the largest dimension,
/// then cropped to fit the precise dimensions specified.
pub fn resize_preserving_aspect(
source_image: &DynamicImage,
new_width: u32,
new_height: u32,
exact_fit: bool,
) -> DynamicImage {
if exact_fit {
source_image.resize_to_fill(new_width, new_height, FilterType::Lanczos3)
} else {
source_image.resize(new_width, new_height, FilterType::Lanczos3)
}
}
#[cfg(test)]
@@ -38,9 +51,7 @@ mod tests {
#[test]
fn downsize_wide_image_preserving_aspect() {
let image = open_wide_image().unwrap();
assert!(image.width() > image.height());
let resized = resize_preserving_aspect(&image, 500, 500);
let resized = resize_preserving_aspect(&image, 500, 500, false);
assert!(resized.width() > resized.height());
assert_eq!(resized.width(), 500);
assert_eq!(resized.height(), 375);
@@ -49,9 +60,7 @@ mod tests {
#[test]
fn downsize_tall_image_preserving_aspect() {
let image = open_tall_image().unwrap();
assert!(image.width() < image.height());
let resized = resize_preserving_aspect(&image, 500, 500);
let resized = resize_preserving_aspect(&image, 500, 500, false);
assert!(resized.width() < resized.height());
assert_eq!(resized.width(), 377);
assert_eq!(resized.height(), 500);
@@ -60,9 +69,7 @@ mod tests {
#[test]
fn enlarge_wide_image_preserving_aspect() {
let image = open_wide_image().unwrap();
assert!(image.width() > image.height());
let resized = resize_preserving_aspect(&image, 1200, 1200);
let resized = resize_preserving_aspect(&image, 1200, 1200, false);
assert!(resized.width() > resized.height());
assert_eq!(resized.width(), 1200);
assert_eq!(resized.height(), 900);
@@ -71,11 +78,57 @@ mod tests {
#[test]
fn enlarge_tall_image_preserving_aspect() {
let image = open_tall_image().unwrap();
assert!(image.width() < image.height());
let resized = resize_preserving_aspect(&image, 1200, 1200);
let resized = resize_preserving_aspect(&image, 1200, 1200, false);
assert!(resized.width() < resized.height());
assert_eq!(resized.width(), 904);
assert_eq!(resized.height(), 1200);
}
#[test]
fn resize_wide_image_preserving_aspect_exact() {
// Downscale
let image = open_wide_image().unwrap();
let resized = resize_preserving_aspect(&image, 500, 500, true);
assert_eq!(resized.width(), 500);
assert_eq!(resized.height(), 500);
// Upscale
let resized = resize_preserving_aspect(&image, 1200, 1200, true);
assert_eq!(resized.width(), 1200);
assert_eq!(resized.height(), 1200);
// Really wide
let resized = resize_preserving_aspect(&image, 1200, 100, true);
assert_eq!(resized.width(), 1200);
assert_eq!(resized.height(), 100);
// Really tall
let resized = resize_preserving_aspect(&image, 100, 1200, true);
assert_eq!(resized.width(), 100);
assert_eq!(resized.height(), 1200);
}
#[test]
fn resize_tall_image_preserving_aspect_exact() {
// Downscale
let image = open_tall_image().unwrap();
let resized = resize_preserving_aspect(&image, 500, 500, true);
assert_eq!(resized.width(), 500);
assert_eq!(resized.height(), 500);
// Upscale
let resized = resize_preserving_aspect(&image, 1200, 1200, true);
assert_eq!(resized.width(), 1200);
assert_eq!(resized.height(), 1200);
// Really wide
let resized = resize_preserving_aspect(&image, 1200, 100, true);
assert_eq!(resized.width(), 1200);
assert_eq!(resized.height(), 100);
// Really tall
let resized = resize_preserving_aspect(&image, 100, 1200, true);
assert_eq!(resized.width(), 100);
assert_eq!(resized.height(), 1200);
}
}
@@ -128,6 +128,8 @@ mod tests {
maybe_preprocess: None,
maybe_make_still: None,
maybe_remove_watermark: None,
maybe_resize_width: None,
maybe_resize_height: None,
})),
};
@@ -34,9 +34,21 @@ pub struct LipsyncArgs {
pub maybe_make_still: Option<bool>,
/// Omit adding the watermark
#[serde(rename = "w")] // NB: DO NOT CHANGE. It could break live jobs. Renamed to be fewer bytes.
#[serde(rename = "m")] // NB: DO NOT CHANGE. It could break live jobs. Renamed to be fewer bytes.
#[serde(skip_serializing_if = "Option::is_none")]
pub maybe_remove_watermark: Option<bool>,
/// Resize width
/// NB: FOR TESTING ONLY
#[serde(rename = "w")] // NB: DO NOT CHANGE. It could break live jobs. Renamed to be fewer bytes.
#[serde(skip_serializing_if = "Option::is_none")]
pub maybe_resize_width: Option<u32>,
/// Resize height
/// NB: FOR TESTING ONLY
#[serde(rename = "h")] // NB: DO NOT CHANGE. It could break live jobs. Renamed to be fewer bytes.
#[serde(skip_serializing_if = "Option::is_none")]
pub maybe_resize_height: Option<u32>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
@@ -152,6 +164,8 @@ mod tests {
maybe_preprocess: None,
maybe_make_still: None,
maybe_remove_watermark: None,
maybe_resize_width: None,
maybe_resize_height: None,
};
let json = serde_json::ser::to_string(&args).unwrap();
assert_eq!(json, r#"{"a":{"F":"audio_media_file"},"i":{"F":"image_media_file"}}"#.to_string());
@@ -167,6 +181,8 @@ mod tests {
maybe_preprocess: None,
maybe_make_still: None,
maybe_remove_watermark: None,
maybe_resize_width: None,
maybe_resize_height: None,
};
let json = serde_json::ser::to_string(&args).unwrap();
assert_eq!(json, r#"{"a":{"U":"audio_media_upload"},"i":{"U":"image_media_upload"}}"#.to_string());
@@ -182,6 +198,8 @@ mod tests {
maybe_preprocess: None,
maybe_make_still: None,
maybe_remove_watermark: None,
maybe_resize_width: None,
maybe_resize_height: None,
};
let json = serde_json::ser::to_string(&args).unwrap();
assert_eq!(json, r#"{"a":{"T":"audio_tts_result"},"i":{"U":"image_media_upload"}}"#.to_string());
@@ -197,6 +215,8 @@ mod tests {
maybe_preprocess: None,
maybe_make_still: None,
maybe_remove_watermark: None,
maybe_resize_width: None,
maybe_resize_height: None,
};
let json = serde_json::ser::to_string(&args).unwrap();
assert_eq!(json, r#"{"a":{"V":"audio_voice_conversion_result"},"i":{"U":"image_media_upload"}}"#.to_string());
@@ -212,6 +232,8 @@ mod tests {
maybe_preprocess: None,
maybe_make_still: None,
maybe_remove_watermark: None,
maybe_resize_width: None,
maybe_resize_height: None,
};
let json = serde_json::ser::to_string(&args).unwrap();
assert_eq!(json, r#"{"f":"G"}"#.to_string());
@@ -227,6 +249,8 @@ mod tests {
maybe_preprocess: None,
maybe_make_still: None,
maybe_remove_watermark: None,
maybe_resize_width: None,
maybe_resize_height: None,
};
let json = serde_json::ser::to_string(&args).unwrap();
assert_eq!(json, r#"{"f":"R"}"#.to_string());
@@ -28,6 +28,7 @@ envvar = { path = "../../../lib/envvar" }
errors = { path = "../../../lib/errors" }
filesys = { path = "../../../lib/files/filesys" }
hashing = { path = "../../../lib/files/hashing" }
images = { path = "../../../lib/files/images" }
jobs_common = { path = "../../../lib/jobs_common" }
media = { path = "../../../lib/files/media" }
memory_caching = { path = "../../../lib/caching/memory_caching" }
@@ -35,6 +36,7 @@ mimetypes = { path = "../../../lib/files/mimetypes" }
mysql_queries = { path = "../../../lib/mysql_queries" }
redis_common = { path = "../../../lib/redis_common" }
subprocess_common = { path = "../../../lib/subprocess_common" }
testing = { path = "../../../lib/testing" }
tokens = { path = "../../../_types/tokens" }
tts_common = { path = "../../../lib/tts_common" }
@@ -20,11 +20,15 @@ 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::download_audio_file::download_audio_file;
use crate::job::job_types::lipsync::sad_talker::download_image_file::download_image_file;
use crate::job::job_types::lipsync::sad_talker::resize_image::resize_image;
use crate::job::job_types::lipsync::sad_talker::sad_talker_inference_command::InferenceArgs;
use crate::job::job_types::lipsync::sad_talker::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;
pub struct SadTalkerProcessJobArgs<'a> {
pub job_dependencies: &'a JobDependencies,
pub job: &'a AvailableInferenceJob,
@@ -102,11 +106,50 @@ pub async fn process_job(args: SadTalkerProcessJobArgs<'_>) -> Result<JobSuccess
&deps.mysql_pool
).await?;
info!("Image file: {:?}", image_path.filesystem_path);
info!("Downloaded image file: {:?}", image_path.filesystem_path);
// ==================== TRANSCODE MEDIA (IF NECESSARY) ==================== //
// TODO
let mut usable_image_path = image_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;
}
}
}
info!("Used image file: {:?}", usable_image_path);
// ==================== SETUP FOR INFERENCE ==================== //
@@ -122,7 +165,7 @@ pub async fn process_job(args: SadTalkerProcessJobArgs<'_>) -> Result<JobSuccess
info!("Expected output video filename: {:?}", &output_video_fs_path);
// TODO: Limit output length for premium.
// TODO: Limit output length for non-premium (???)
let maybe_args = job.maybe_inference_args
.as_ref()
@@ -143,7 +186,7 @@ pub async fn process_job(args: SadTalkerProcessJobArgs<'_>) -> Result<JobSuccess
.inference_command
.execute_inference(InferenceArgs {
input_audio: &audio_path.filesystem_path,
input_image: &image_path.filesystem_path,
input_image: &usable_image_path,
work_dir: &workdir,
output_file: &output_video_fs_path,
stderr_output_file: &stderr_output_file,
@@ -170,6 +213,7 @@ pub async fn process_job(args: SadTalkerProcessJobArgs<'_>) -> Result<JobSuccess
safe_delete_temp_file(&audio_path.filesystem_path);
safe_delete_temp_file(&image_path.filesystem_path);
safe_delete_temp_file(&usable_image_path);
safe_delete_temp_file(&output_video_fs_path);
safe_delete_temp_file(&stderr_output_file);
safe_delete_temp_directory(&work_temp_dir);
@@ -207,6 +251,7 @@ pub async fn process_job(args: SadTalkerProcessJobArgs<'_>) -> Result<JobSuccess
error!("Watermark failed: {:?}", command_exit_status);
safe_delete_temp_file(&audio_path.filesystem_path);
safe_delete_temp_file(&image_path.filesystem_path);
safe_delete_temp_file(&usable_image_path);
safe_delete_temp_file(&output_video_fs_path);
safe_delete_temp_file(&output_video_fs_path_watermark);
safe_delete_temp_directory(&work_temp_dir);
@@ -263,6 +308,7 @@ pub async fn process_job(args: SadTalkerProcessJobArgs<'_>) -> Result<JobSuccess
safe_delete_temp_file(&output_video_fs_path);
safe_delete_temp_file(&output_video_fs_path_watermark);
safe_delete_temp_file(&usable_image_path);
// NB: We should be using a tempdir, but to make absolutely certain we don't overflow the disk...
safe_delete_temp_directory(&work_temp_dir);
@@ -1,8 +1,28 @@
use std::path::Path;
use std::path::{Path, PathBuf};
use log::info;
use tempdir::TempDir;
use errors::AnyhowResult;
use images::image::ImageFormat;
use images::resize_image_file_preserving_aspect::resize_image_file_preserving_aspect;
pub fn resize_image<P: AsRef<Path>, Q: AsRef<Path>>(input_file: P, output_file: Q) -> AnyhowResult<()> {
pub fn resize_image<P: AsRef<Path>>(
input_file: P,
tempdir: &TempDir,
new_width: u32,
new_height: u32
) -> AnyhowResult<PathBuf> {
let image = resize_image_file_preserving_aspect(
input_file,
new_width,
new_height,
true
)?;
let output_file = tempdir.path().join("resized_image.png");
info!("File successfully resized to {}x{} !", image.width(), image.height());
image.save_with_format(&output_file, ImageFormat::Png)?;
Ok(output_file)
}
@@ -15,6 +15,9 @@ pub struct JobArgs<'a> {
pub preprocess: Option<String>,
// NB: Enhancer controls quality
pub enhancer: Option<String>,
pub width: Option<u32>,
pub height: Option<u32>,
}
pub fn validate_job(job: &AvailableInferenceJob) -> Result<JobArgs, ProcessSingleJobError> {
@@ -70,7 +73,7 @@ pub fn validate_job(job: &AvailableInferenceJob) -> Result<JobArgs, ProcessSingl
let make_still = inference_args.maybe_make_still.unwrap_or(false);
let preprocess = match inference_args.maybe_preprocess {
None => None,
None => Some("full".to_string()),
Some(Preprocess::F) => Some("full".to_string()),
Some(Preprocess::EF) => Some("extfull".to_string()),
Some(Preprocess::C) => Some("crop".to_string()),
@@ -83,6 +86,9 @@ pub fn validate_job(job: &AvailableInferenceJob) -> Result<JobArgs, ProcessSingl
Some(FaceEnhancer::R) => Some("RestoreFormer".to_string()),
};
let mut width = inference_args.maybe_resize_width;
let mut height = inference_args.maybe_resize_height;
Ok(JobArgs {
audio_source,
image_source,
@@ -90,5 +96,7 @@ pub fn validate_job(job: &AvailableInferenceJob) -> Result<JobArgs, ProcessSingl
make_still,
preprocess,
enhancer,
width,
height,
})
}
}
@@ -47,6 +47,8 @@ pub struct Plan {
lipsync_requires_frontend_keepalive: bool,
can_remove_visual_watermarks: bool,
// ========== Features for TTS ==========
tts_base_priority_level: u8,
@@ -89,6 +91,7 @@ impl Plan {
is_development_plan: builder.is_development_plan,
is_synthetic_plan: builder.is_synthetic_plan,
lipsync_requires_frontend_keepalive: builder.lipsync_requires_frontend_keepalive,
can_remove_visual_watermarks: builder.can_remove_visual_watermarks,
tts_base_priority_level: builder.tts_base_priority_level,
tts_max_duration: builder.tts_max_duration,
tts_max_character_length: builder.tts_max_character_length,
@@ -143,6 +146,10 @@ impl Plan {
self.lipsync_requires_frontend_keepalive
}
pub fn can_remove_visual_watermarks(&self) -> bool {
self.can_remove_visual_watermarks
}
pub fn tts_base_priority_level(&self) -> u8 {
self.tts_base_priority_level
}
@@ -183,6 +190,8 @@ pub struct PlanBuilder {
lipsync_requires_frontend_keepalive: bool,
can_remove_visual_watermarks: bool,
// ========== Features for TTS ==========
tts_base_priority_level: u8,
@@ -228,6 +237,7 @@ impl PlanBuilder {
// Lipsync (SadTalker, not Wav2Lip)
lipsync_requires_frontend_keepalive: true,
can_remove_visual_watermarks: false,
// TTS
tts_base_priority_level : TTS_DEFAULT_PRIORITY_LEVEL,
@@ -294,6 +304,11 @@ impl PlanBuilder {
self
}
pub fn can_remove_visual_watermarks(mut self, value: bool) -> Self {
self.can_remove_visual_watermarks = value;
self
}
pub fn tts_base_priority_level(mut self, value: u8) -> Self {
self.tts_base_priority_level = value;
self
@@ -146,6 +146,7 @@ pub static PRODUCTION_PREMIUM_PLANS: Lazy<HashSet<Plan>> = Lazy::new(|| {
.web_vc_base_priority_level(30)
.web_vc_requires_frontend_keepalive(false)
.lipsync_requires_frontend_keepalive(false)
.can_remove_visual_watermarks(true)
.build());
plans.insert(PlanBuilder::new("fakeyou_elite")
@@ -159,6 +160,7 @@ pub static PRODUCTION_PREMIUM_PLANS: Lazy<HashSet<Plan>> = Lazy::new(|| {
.web_vc_base_priority_level(40)
.web_vc_requires_frontend_keepalive(false)
.lipsync_requires_frontend_keepalive(false)
.can_remove_visual_watermarks(true)
.build());
// ========== Spanish plans ==========
@@ -16,17 +16,15 @@ use enums::common::visibility::Visibility;
use http_server_common::request::get_request_header_optional::get_request_header_optional;
use http_server_common::request::get_request_ip::get_request_ip;
use mysql_queries::payloads::generic_inference_args::generic_inference_args::{GenericInferenceArgs, InferenceCategoryAbbreviated, PolymorphicInferenceArgs};
use mysql_queries::payloads::generic_inference_args::lipsync_payload::{FaceEnhancer, LipsyncAnimationAudioSource, LipsyncAnimationImageSource, LipsyncArgs, Preprocess};
use mysql_queries::payloads::generic_inference_args::lipsync_payload::{FaceEnhancer, LipsyncAnimationAudioSource, LipsyncAnimationImageSource, LipsyncArgs};
use mysql_queries::queries::generic_inference::web::insert_generic_inference_job::{insert_generic_inference_job, InsertGenericInferenceArgs};
use tokens::files::media_upload::MediaUploadToken;
use tokens::jobs::inference::InferenceJobToken;
use tokens::tokens::media_files::MediaFileToken;
use tokens::tokens::voice_conversion_results::VoiceConversionResultToken;
use tokens::users::user::UserToken;
use tts_common::priority::FAKEYOU_INVESTOR_PRIORITY_LEVEL;
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
use crate::http_server::endpoints::investor_demo::demo_cookie::request_has_demo_cookie;
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
use crate::server_state::ServerState;
@@ -46,30 +44,60 @@ pub struct EnqueueLipsyncAnimationRequest {
image_source: ImageSource,
creator_set_visibility: Option<Visibility>,
is_storyteller_demo: Option<bool>,
/// SadTalker: cropping
crop: Option<CropMode>,
/// Width/height of the video to generate (presets)
dimensions: Option<FrameSize>,
/// SadTalker: parameter to make the animation more still.
make_still: Option<bool>,
/// SadTalker: parameter to enhance the face using gfpgan.
high_quality: Option<bool>,
/// SadTalker: we use gfpgan face enhancement by default; this disables it.
disable_face_enhancement: Option<bool>,
/// Remove the watermark
/// Remove the watermark (premium only)
remove_watermark: Option<bool>,
///// SadTalker: cropping
//crop: Option<CropMode>,
}
//#[derive(Deserialize, Copy, Clone)]
//#[serde(rename_all = "snake_case")]
//pub enum CropMode {
// /// SadTalker: "extcrop", which appears to be a wider crop than the "crop" option
// Crop,
// /// SadTalker: "crop" (the default)
// CloseCrop,
// /// SadTalker: "full" (can't tell that "extfull" does any differently)
// Full,
//}
#[derive(Deserialize, Copy, Clone)]
#[serde(rename_all = "snake_case")]
pub enum CropMode {
/// SadTalker: "extcrop", which appears to be a wider crop than the "crop" option
Crop,
/// SadTalker: "crop" (the default)
CloseCrop,
/// SadTalker: "full" (can't tell that "extfull" does any differently)
Full,
pub enum FrameSize {
/// Twitter horizontal = 1280x720
/// See: https://developer.twitter.com/en/docs/twitter-api/v1/media/upload-media/uploading-media/media-best-practices
TwitterLandscape,
/// Twitter vertical = 720x1280
TwitterPortrait,
/// Twitter square = 720x720
TwitterSquare,
// D-ID: 1000x1000
// Runway: 896x512
// Pika: 1024x576
}
impl FrameSize {
pub fn get_width_and_height(&self) -> (u32, u32) {
match self {
FrameSize::TwitterLandscape => (1280, 720),
FrameSize::TwitterPortrait => (720, 1280),
FrameSize::TwitterSquare => (720, 720),
}
}
}
/// Treated as an enum. Only one of these may be set.
@@ -137,8 +165,6 @@ pub async fn enqueue_lipsync_animation_handler(
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, EnqueueLipsyncAnimationError>
{
let mut maybe_user_token : Option<UserToken> = None;
let mut priority_level ;
let disable_rate_limiter = false; // NB: Careful!
let mut mysql_connection = server_state.mysql_pool
.acquire()
@@ -169,33 +195,7 @@ pub async fn enqueue_lipsync_animation_handler(
maybe_user_session.as_ref());
// TODO: Separate priority for animation.
priority_level = plan.web_vc_base_priority_level();
// ==================== INVESTOR PRIORITY ==================== //
// TODO/TEMP: Give investors even more priority
let mut is_investor = false;
{
// TODO/TEMP: The storyteller.io website's AJAX calls will set this.
// This is just for the YCombinator demo.
match request.is_storyteller_demo {
Some(true) => {
is_investor = true;
},
_ => {},
};
// TODO/TEMP: The storyteller.io website will redirect and establish this cookie.
// This is just for the YCombinator demo.
if request_has_demo_cookie(&http_request) {
is_investor = true;
}
if is_investor {
priority_level = FAKEYOU_INVESTOR_PRIORITY_LEVEL;
}
}
let priority_level = plan.web_vc_base_priority_level();
// ==================== DEBUG MODE + ROUTING TAG ==================== //
@@ -209,25 +209,18 @@ pub async fn enqueue_lipsync_animation_handler(
// ==================== RATE LIMIT ==================== //
if !disable_rate_limiter {
let mut rate_limiter = match maybe_user_session {
None => &server_state.redis_rate_limiters.logged_out,
Some(ref user) => {
if user.role.is_banned {
return Err(EnqueueLipsyncAnimationError::NotAuthorized);
}
&server_state.redis_rate_limiters.logged_in
},
};
let rate_limiter = match maybe_user_session {
None => &server_state.redis_rate_limiters.logged_out,
Some(ref user) => {
if user.role.is_banned {
return Err(EnqueueLipsyncAnimationError::NotAuthorized);
}
&server_state.redis_rate_limiters.logged_in
},
};
// TODO/TEMP
if is_investor {
rate_limiter = &server_state.redis_rate_limiters.logged_in;
}
if let Err(_err) = rate_limiter.rate_limit_request(&http_request) {
return Err(EnqueueLipsyncAnimationError::RateLimited);
}
if let Err(_err) = rate_limiter.rate_limit_request(&http_request) {
return Err(EnqueueLipsyncAnimationError::RateLimited);
}
// ==================== LOOK UP MODEL INFO ==================== //
@@ -276,22 +269,16 @@ pub async fn enqueue_lipsync_animation_handler(
maybe_preprocess: None,
maybe_make_still: None,
maybe_remove_watermark: None,
maybe_resize_width: None,
maybe_resize_height: None,
};
if request.high_quality.unwrap_or(false) {
let enable_face_enhancement = !request.disable_face_enhancement.unwrap_or(false);
if enable_face_enhancement {
inference_args.maybe_face_enhancer = Some(FaceEnhancer::G); // "--enhancer gfpgan"
}
if let Some(crop_mode) = request.crop {
let crop_mode = match crop_mode {
CropMode::Crop => Preprocess::EC, // NB: "extcrop" is less of a close crop than "crop"
CropMode::CloseCrop => Preprocess::C,
CropMode::Full => Preprocess::F,
};
inference_args.maybe_preprocess = Some(crop_mode);
}
if request.make_still.unwrap_or(false) {
inference_args.maybe_make_still = Some(true);
}
@@ -300,6 +287,13 @@ pub async fn enqueue_lipsync_animation_handler(
inference_args.maybe_remove_watermark = Some(true);
}
let (width, height) = request.dimensions
.unwrap_or(FrameSize::TwitterLandscape)
.get_width_and_height();
inference_args.maybe_resize_width = Some(width);
inference_args.maybe_resize_height = Some(height);
info!("Creating lipsync animation job record...");
let query_result = insert_generic_inference_job(InsertGenericInferenceArgs {