truncate audio

This commit is contained in:
Brandon Thomas committed 2024-08-07 19:17:44 -04:00
1 parent 7f95c86e31
commit 2ee6822bac
7 files changed
+72 -92

No files matched your search

@@ -154,38 +154,6 @@ impl GenericInferenceArgs {
}
}
//impl InferenceCategoryAbbreviated {
// pub fn from_inference_category(category: InferenceCategory) -> Self {
// match category {
// InferenceCategory::LipsyncAnimation => Self::LipsyncAnimation,
// InferenceCategory::TextToSpeech => Self::TextToSpeech,
// InferenceCategory::VoiceConversion => Self::VoiceConversion,
// InferenceCategory::VideoFilter => Self::VideoFilter,
// InferenceCategory::ImageGeneration => Self::ImageGeneration,
// InferenceCategory::Mocap => Self::Mocap,
// InferenceCategory::Workflow => Self::Workflow,
// InferenceCategory::FormatConversion => Self::FormatConversion,
// InferenceCategory::ConvertBvhToWorkflow => Self::ConvertBvhToWorkflow,
// InferenceCategory::DeprecatedField => Self::LivePortrait, // TODO(bt,2024-07-16): This isn't correct
// }
// }
//
// pub fn to_inference_category(self) -> InferenceCategory {
// match self {
// Self::LipsyncAnimation => InferenceCategory::LipsyncAnimation,
// Self::TextToSpeech => InferenceCategory::TextToSpeech,
// Self::VoiceConversion => InferenceCategory::VoiceConversion,
// Self::VideoFilter => InferenceCategory::VideoFilter,
// Self::ImageGeneration =>InferenceCategory::ImageGeneration,
// Self::Mocap => InferenceCategory::Mocap,
// Self::Workflow => InferenceCategory::Workflow,
// Self::LivePortrait => InferenceCategory::DeprecatedField, // TODO(bt,2024-07-16): This isn't correct
// Self::FormatConversion => InferenceCategory::FormatConversion,
// Self::ConvertBvhToWorkflow => InferenceCategory::ConvertBvhToWorkflow,
// }
// }
//}
#[cfg(test)]
mod tests {
use tokens::tokens::{media_files::MediaFileToken, model_weights::ModelWeightToken};
@@ -13,5 +13,16 @@ pub struct GptSovitsPayload {
#[serde(rename = "cv")]
#[serde(skip_serializing_if = "Option::is_none")]
pub creator_visibility: Option<Visibility>,
// For free users, we may truncate long TTS inferences.
#[serde(rename = "s")]
#[serde(skip_serializing_if = "Option::is_none")]
pub truncate_seconds: Option<u64>,
// For free users, we may occasionally append an advertisement
// to sign in or upgrade.
#[serde(rename = "a")]
#[serde(skip_serializing_if = "Option::is_none")]
pub append_advertisement: Option<bool>,
}
@@ -1,9 +1,11 @@
use errors::AnyhowResult;
use crate::job::job_types::gpt_sovits::gpt_sovits_inference_command::GptSovitsInferenceCommand;
use crate::util::common_commands::ffmpeg::runner::ffmpeg_command_runner::FfmpegCommandRunner;
pub struct GptSovitsDependencies {
pub inference_command: GptSovitsInferenceCommand,
pub ffmpeg_command_runner: FfmpegCommandRunner,
}
@@ -11,6 +13,7 @@ impl GptSovitsDependencies {
pub fn setup() -> AnyhowResult<Self> {
Ok(Self {
inference_command: GptSovitsInferenceCommand::from_env()?,
ffmpeg_command_runner: FfmpegCommandRunner::from_env()?,
})
}
}
@@ -1,6 +1,6 @@
use anyhow::anyhow;
use mysql_queries::payloads::generic_inference_args::generic_inference_args::InferenceCategoryAbbreviated;
use mysql_queries::payloads::generic_inference_args::generic_inference_args::{InferenceCategoryAbbreviated, PolymorphicInferenceArgs};
use mysql_queries::queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob;
use tokens::tokens::model_weights::ModelWeightToken;
@@ -13,14 +13,11 @@ pub struct JobArgs {
pub reference_free: Option<bool>,
pub temperature: Option<f32>,
pub target_language: Option<String>,
pub maybe_truncate_seconds: Option<u64>,
pub maybe_append_advertisement: Option<bool>,
}
pub fn check_and_validate_job(job: &AvailableInferenceJob) -> Result<JobArgs, ProcessSingleJobError> {
let inference_args = job.maybe_inference_args
.as_ref()
.map(|args| args.args.as_ref())
.flatten();
let inference_category = job.maybe_inference_args
.as_ref()
.map(|args| args.inference_category)
@@ -34,20 +31,17 @@ pub fn check_and_validate_job(job: &AvailableInferenceJob) -> Result<JobArgs, Pr
}
};
// let inference_args = match inference_args {
// Some(args) => args,
// None => {
// return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("no inference args for job!")));
// }
// };
//
// let inference_args = match inference_args {
// PolymorphicInferenceArgs::Gs(inference_args) => inference_args,
// _ => {
// return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("wrong inner args for job!")));
// }
// };
//
let inference_args = job.maybe_inference_args
.as_ref()
.map(|args| args.args.as_ref())
.flatten()
.map(|args| match args {
PolymorphicInferenceArgs::Gs(args) => Ok(args),
_ => Err(ProcessSingleJobError::from_anyhow_error(anyhow!("wrong payload for job!"))),
})
.transpose()?
.ok_or_else(|| ProcessSingleJobError::from_anyhow_error(anyhow!("no inference args for job!")))?;
let input_text = match &job.maybe_raw_inference_text {
Some(text) => text.clone(),
None => {
@@ -62,12 +56,13 @@ pub fn check_and_validate_job(job: &AvailableInferenceJob) -> Result<JobArgs, Pr
}
};
Ok(JobArgs {
input_text: input_text,
gpt_sovits_model: gpt_sovits_model,
input_text,
gpt_sovits_model,
reference_free: None,
temperature: None,
target_language: None,
maybe_truncate_seconds: inference_args.truncate_seconds.clone(),
maybe_append_advertisement: inference_args.append_advertisement.clone(),
})
}
}
@@ -11,6 +11,7 @@ use enums::by_table::generic_inference_jobs::inference_result_type::InferenceRes
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
use enums::by_table::media_files::media_file_type::MediaFileType;
use filesys::check_file_exists::check_file_exists;
use filesys::file_exists::file_exists;
use filesys::file_size::file_size;
use hashing::sha256::sha256_hash_file::sha256_hash_file;
use media::decode_basic_audio_info::decode_basic_audio_file_info;
@@ -27,6 +28,7 @@ use crate::job::job_types::gpt_sovits::model_package::model_package::GptSovitsPa
use crate::job::job_types::gpt_sovits::tts_inference::check_and_validate_job::check_and_validate_job;
use crate::state::job_dependencies::JobDependencies;
use crate::util::common_commands::ffmpeg::ffmpeg_audio_replace_args::FfmpegAudioReplaceArgs;
use crate::util::common_commands::ffmpeg::ffmpeg_audio_truncate_args::FfmpegAudioTruncateArgs;
const BUCKET_FILE_PREFIX: &str = "fakeyou_";
const BUCKET_FILE_EXTENSION: &str = ".wav";
@@ -132,7 +134,6 @@ pub async fn process_single_gpt_sovits_tts_job(
reference_audio_path: reference_audio_path.as_deref(),
reference_transcript_path: reference_transcript_path.as_deref(),
reference_language: Some(DEFAULT_LANGUAGE),
//output_audio_directory: &output_dir,
output_file_path: &output_file_path,
maybe_reference_free: None,
maybe_temperature: None,
@@ -160,27 +161,44 @@ pub async fn process_single_gpt_sovits_tts_job(
info!("Inference was successful.");
// ==================== MAYBE TRUNCATE FILE ==================== //
//let command_exit_status = args
// .comfy_deps
// .ffmpeg_command_runner
// .run_with_subprocess(RunAsSubprocessArgs {
// args: Box::new(&FfmpegAudioReplaceArgs {
// input_video_file: &args.videos.primary_video.comfy_output_video_path,
// input_audio_file: &input_video_file,
// output_video_file: &output_video_fs_path_restored,
// }),
// stderr: StreamRedirection::None,
// stdout: StreamRedirection::None,
// });
// NB: This will be the final audio file we upload to the bucket
let mut audio_for_upload_path = output_file_path.clone();
if let Some(truncate_seconds) = job_args.maybe_truncate_seconds {
if truncate_seconds > 0 {
let truncated_audio_output_path = output_dir.join("truncated.wav");
let command_exit_status = gpt_sovits_deps
.ffmpeg_command_runner
.run_with_subprocess(RunAsSubprocessArgs {
args: Box::new(&FfmpegAudioTruncateArgs {
input_audio_file: &output_file_path,
output_audio_file: &truncated_audio_output_path,
truncate_seconds: truncate_seconds as usize,
}),
stderr: StreamRedirection::Pipe,
stdout: StreamRedirection::Pipe,
});
if !command_exit_status.is_success() {
warn!("Error truncating audio file. Exit status: {:?}", command_exit_status);
}
if file_exists(&truncated_audio_output_path) {
info!("Truncated audio file created successfully.");
audio_for_upload_path = truncated_audio_output_path;
} else {
warn!("Truncated audio file does not exist: {:?}", &truncated_audio_output_path);
}
}
}
// ==================== DECODE AUDIO FILE ==================== //
let maybe_audio_info = decode_basic_audio_file_info(
&output_file_path, Some(MIME_TYPE), Some("wav"))
&audio_for_upload_path, Some(MIME_TYPE), Some("wav"))
.map_err(|err| {
warn!("Error decoding audio info: {:?}", err);
err
@@ -210,13 +228,13 @@ pub async fn process_single_gpt_sovits_tts_job(
let result_bucket_object_pathbuf = result_bucket_location.to_full_object_pathbuf();
// Finished file path
info!("Upload File Path: {:?}", &audio_for_upload_path);
info!("Upload Bucket Path: {:?}", result_bucket_object_pathbuf);
info!("Upload File Path: {:?}", &output_file_path);
job_dependencies.buckets.public_bucket_client
.upload_filename_with_content_type(
&result_bucket_object_pathbuf,
&output_file_path,
&audio_for_upload_path,
&MIME_TYPE
)
.await
@@ -226,11 +244,11 @@ pub async fn process_single_gpt_sovits_tts_job(
info!("Calculating sha256...");
let file_checksum = sha256_hash_file(&output_file_path).map_err(|err| {
let file_checksum = sha256_hash_file(&audio_for_upload_path).map_err(|err| {
ProcessSingleJobError::Other(anyhow!("Error hashing file: {:?}", err))
})?;
let file_size_bytes = file_size(&output_file_path).map_err(|err|
let file_size_bytes = file_size(&audio_for_upload_path).map_err(|err|
ProcessSingleJobError::Other(err)
)?;
@@ -3,13 +3,13 @@ use std::path::Path;
use filesys::path_to_string::path_to_string;
use subprocess_common::command_runner::command_args::CommandArgs;
pub struct FfmpegAudioReplaceArgs<'a> {
pub struct FfmpegAudioTruncateArgs<'a> {
pub input_audio_file: &'a Path,
pub output_audio_file: &'a Path,
pub truncate_seconds: usize,
}
impl CommandArgs for FfmpegAudioReplaceArgs<'_> {
impl CommandArgs for FfmpegAudioTruncateArgs<'_> {
// eg. ffmpeg -i input-audio.aac -t 15 -c copy output.aac
// time is in seconds
@@ -11,21 +11,6 @@ use errors::AnyhowResult;
use filesys::path_to_string::path_to_string;
use subprocess_common::command_exit_status::CommandExitStatus;
// These environment vars are not copied over to the subprocess
// TODO/FIXME(bt, 2023-05-28): This is horrific security!
static IGNORED_ENVIRONMENT_VARS : Lazy<HashSet<String>> = Lazy::new(|| {
let env_var_names= [
"MYSQL_URL",
"ACCESS_KEY",
"SECRET_KEY",
"NEWRELIC_API_KEY",
];
env_var_names.iter()
.map(|value| value.to_string())
.collect::<HashSet<String>>()
});
#[derive(Clone)]
pub struct FfmpegLogoWatermarkCommand {
/// A single executable script or a much larger bash command.