mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 17:38:10 +00:00
Merge pull request #170 from storytold/brandon-seed-tt2-models
seed tool: TT2 models (with idempotency + early exit)
This commit is contained in:
17 files changed
+432
-56
No files matched your search
@@ -2,10 +2,13 @@
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- DO NOT DO THIS IN PRODUCTION.
|
||||
-- Just placing this here to be easy to copy/paste in development.
|
||||
truncate zs_voices;
|
||||
truncate zs_voice_datasets;
|
||||
truncate zs_voice_dataset_samples;
|
||||
truncate media_files;
|
||||
truncate media_uploads;
|
||||
truncate generic_inference_jobs;
|
||||
truncate tts_models;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use log::info;
|
||||
|
||||
use cloud_storage::bucket_client::BucketClient;
|
||||
use errors::AnyhowResult;
|
||||
|
||||
pub struct BucketClients {
|
||||
pub public: BucketClient,
|
||||
pub private: BucketClient,
|
||||
}
|
||||
|
||||
pub fn get_bucket_clients() -> AnyhowResult<BucketClients> {
|
||||
let access_key = easyenv::get_env_string_required("ACCESS_KEY")?;
|
||||
let secret_key = easyenv::get_env_string_required("SECRET_KEY")?;
|
||||
let region_name = easyenv::get_env_string_required("REGION_NAME")?;
|
||||
let public_bucket_name = easyenv::get_env_string_required("PUBLIC_BUCKET_NAME")?;
|
||||
let private_bucket_name = easyenv::get_env_string_required("PRIVATE_BUCKET_NAME")?;
|
||||
|
||||
// NB: Long timeout for dev rust builds to upload to cloud buckets.
|
||||
// Unoptimized binaries sometimes take a lot of time to upload, presumably due to unoptimized code.
|
||||
let bucket_timeout = easyenv::get_env_duration_seconds_or_default(
|
||||
"BUCKET_TIMEOUT_SECONDS", Duration::from_secs(60 * 10));
|
||||
|
||||
info!("Configuring public GCS bucket...");
|
||||
|
||||
let public_bucket_client = BucketClient::create(
|
||||
&access_key,
|
||||
&secret_key,
|
||||
®ion_name,
|
||||
&public_bucket_name,
|
||||
None,
|
||||
Some(bucket_timeout),
|
||||
)?;
|
||||
|
||||
info!("Configuring private GCS bucket...");
|
||||
|
||||
let private_bucket_client = BucketClient::create(
|
||||
&access_key,
|
||||
&secret_key,
|
||||
®ion_name,
|
||||
&private_bucket_name,
|
||||
None,
|
||||
Some(bucket_timeout),
|
||||
)?;
|
||||
|
||||
Ok(BucketClients {
|
||||
public: public_bucket_client,
|
||||
private: private_bucket_client,
|
||||
})
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
use std::time::Duration;
|
||||
use elasticsearch::Elasticsearch;
|
||||
|
||||
use log::info;
|
||||
use sqlx::mysql::MySqlPoolOptions;
|
||||
|
||||
use cloud_storage::bucket_client::BucketClient;
|
||||
use config::shared_constants::{DEFAULT_MYSQL_CONNECTION_STRING, DEFAULT_RUST_LOG};
|
||||
use errors::AnyhowResult;
|
||||
use crate::cli_args::parse_cli_args;
|
||||
|
||||
use crate::bucket_clients::get_bucket_clients;
|
||||
use crate::cli_args::parse_cli_args;
|
||||
use crate::seeding::tts_tacotron2::seed_tts_tacotron2;
|
||||
use crate::seeding::users::seed_user_accounts;
|
||||
use crate::seeding::voice_conversion::seed_voice_conversion;
|
||||
use crate::seeding::zero_shot_tts::seed_zero_shot_tts;
|
||||
|
||||
pub mod bucket_clients;
|
||||
pub mod cli_args;
|
||||
pub mod seeding;
|
||||
|
||||
@@ -37,10 +37,10 @@ pub async fn main() -> AnyhowResult<()> {
|
||||
|
||||
let args = parse_cli_args()?;
|
||||
|
||||
let mut maybe_public_bucket_client = None;
|
||||
let mut maybe_bucket_clients = None;
|
||||
|
||||
if args.seed_cloud_bucket {
|
||||
maybe_public_bucket_client = Some(get_bucket_client()?);
|
||||
maybe_bucket_clients = Some(get_bucket_clients()?);
|
||||
}
|
||||
|
||||
let mut maybe_elasticsearch = None;
|
||||
@@ -50,36 +50,14 @@ pub async fn main() -> AnyhowResult<()> {
|
||||
}
|
||||
|
||||
seed_user_accounts(&pool).await?;
|
||||
seed_zero_shot_tts(&pool, maybe_public_bucket_client.as_ref()).await?;
|
||||
seed_zero_shot_tts(&pool, maybe_bucket_clients.as_ref()).await?;
|
||||
seed_voice_conversion(&pool).await?;
|
||||
seed_tts_tacotron2(&pool, maybe_bucket_clients.as_ref()).await?;
|
||||
|
||||
info!("Done!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_bucket_client() -> AnyhowResult<BucketClient> {
|
||||
let access_key = easyenv::get_env_string_required("ACCESS_KEY")?;
|
||||
let secret_key = easyenv::get_env_string_required("SECRET_KEY")?;
|
||||
let region_name = easyenv::get_env_string_required("REGION_NAME")?;
|
||||
let public_bucket_name = easyenv::get_env_string_required("PUBLIC_BUCKET_NAME")?;
|
||||
|
||||
let bucket_timeout = easyenv::get_env_duration_seconds_or_default(
|
||||
"BUCKET_TIMEOUT_SECONDS", Duration::from_secs(60 * 5));
|
||||
|
||||
info!("Configuring GCS bucket...");
|
||||
|
||||
let public_bucket_client = BucketClient::create(
|
||||
&access_key,
|
||||
&secret_key,
|
||||
®ion_name,
|
||||
&public_bucket_name,
|
||||
None,
|
||||
Some(bucket_timeout),
|
||||
)?;
|
||||
|
||||
Ok(public_bucket_client)
|
||||
}
|
||||
|
||||
fn get_elasticsearch_client() -> AnyhowResult<Elasticsearch> {
|
||||
// TODO(bt,2023-10-26): Allow connecting to instances by URL instead of the default dev URL.
|
||||
let client = Elasticsearch::default();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod tts_tacotron2;
|
||||
pub mod users;
|
||||
pub mod voice_conversion;
|
||||
pub mod zero_shot_tts;
|
||||
@@ -0,0 +1,181 @@
|
||||
use std::path::Path;
|
||||
|
||||
use log::{info, warn};
|
||||
use sqlx::{MySql, Pool};
|
||||
|
||||
use cloud_storage::bucket_path_unifier::BucketPathUnifier;
|
||||
use enums::by_table::tts_models::tts_model_type::TtsModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use errors::{anyhow, AnyhowResult};
|
||||
use filesys::file_read_bytes::file_read_bytes;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use hashing::sha256::sha256_hash_file::sha256_hash_file;
|
||||
use mimetypes::mimetype_for_bytes::get_mimetype_for_bytes;
|
||||
use mysql_queries::queries::tts::tts_models::get_tts_model::get_tts_model_by_token;
|
||||
use mysql_queries::queries::tts::tts_models::insert_tts_model_from_download_job::{insert_tts_model_from_download_job, InsertTtsModelFromDownloadJobArgs};
|
||||
use storyteller_root::get_seed_tool_data_root;
|
||||
use tokens::tokens::tts_models::TtsModelToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::bucket_clients::BucketClients;
|
||||
use crate::seeding::users::HANASHI_USER_TOKEN;
|
||||
|
||||
pub async fn seed_tts_tacotron2(mysql_pool: &Pool<MySql>, maybe_bucket_clients: Option<&BucketClients>) -> AnyhowResult<()> {
|
||||
info!("Seeding TTS (Tacotron2)...");
|
||||
|
||||
// NB: This is idempotent and will only install the models once.
|
||||
let tts_models = [
|
||||
("Mai Valentine from Yugioh (by Vegito)", HANASHI_USER_TOKEN, "TM:cknxtj6mx3t3", "models/tts/tacotron2/MaiYugioh.pth"),
|
||||
("Nagito (by Vegito)", HANASHI_USER_TOKEN, "TM:tmhxapghwaf3", "models/tts/tacotron2/Nagito.pth"),
|
||||
("Son Goku V2 (by Vegito, Arpabet version)", HANASHI_USER_TOKEN, "TM:kbvaa4726jdw", "models/tts/tacotron2/SonGokuV2.pth"),
|
||||
("Vegeta (by Vegito, Arpabet version)", HANASHI_USER_TOKEN, "TM:kvv9xqashp6n", "models/tts/tacotron2/VegetaNew.pth"),
|
||||
];
|
||||
|
||||
let seed_tool_data_root = get_seed_tool_data_root();
|
||||
|
||||
// TODO(bt,2023-11-27): DO NOT USE BucketPathUnifier for any new code. Only for TT2 and super legacy systems.
|
||||
let bucket_path_unifier = BucketPathUnifier::default_paths();
|
||||
|
||||
for (model_title, user_token, tts_model_token, subdirectory_path) in tts_models {
|
||||
let user_token = UserToken::new_from_str(user_token);
|
||||
let tts_model_token = TtsModelToken::new_from_str(tts_model_token);
|
||||
|
||||
let mut model_file_path = seed_tool_data_root.clone();
|
||||
model_file_path.push(subdirectory_path);
|
||||
|
||||
let result = seed_model(
|
||||
&mysql_pool,
|
||||
&tts_model_token,
|
||||
model_title,
|
||||
&user_token,
|
||||
&model_file_path,
|
||||
maybe_bucket_clients,
|
||||
&bucket_path_unifier
|
||||
).await;
|
||||
|
||||
match result {
|
||||
Ok(_) => info!("Seeded {}", model_title),
|
||||
Err(err) => warn!(r#"
|
||||
Could not TTS model {} : {:?}
|
||||
(No worries: if there's a duplicate key error, we probably already
|
||||
seeded the TTS model on a previous invocation!)
|
||||
"#, model_title, err),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn seed_model(
|
||||
mysql_pool: &Pool<MySql>,
|
||||
tts_model_token: &TtsModelToken,
|
||||
model_title: &str,
|
||||
user_token: &UserToken,
|
||||
model_file_path: &Path,
|
||||
maybe_bucket_clients: Option<&BucketClients>,
|
||||
bucket_path_unifier: &BucketPathUnifier,
|
||||
) -> AnyhowResult<()> {
|
||||
info!("Seeding TTS model {} ...", model_title);
|
||||
|
||||
let mut private_bucket_hash = "NOT_UPLOADED_BY_SEED_TOOL".to_string();
|
||||
let mut private_bucket_object_name = "NOT_UPLOADED_BY_SEED_TOOL".to_string();
|
||||
|
||||
if tts_model_exists(mysql_pool, tts_model_token).await? {
|
||||
info!("Model already seeded: {:?}", tts_model_token);
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
if let Some(bucket_clients) = maybe_bucket_clients {
|
||||
let bucket_details = seed_file_to_bucket(model_file_path, bucket_clients, bucket_path_unifier).await?;
|
||||
private_bucket_hash = bucket_details.bucket_hash;
|
||||
private_bucket_object_name = bucket_details.bucket_object_name;
|
||||
}
|
||||
|
||||
let filename = model_file_path
|
||||
.file_name()
|
||||
.map(|name| name.to_str())
|
||||
.flatten()
|
||||
.unwrap_or("");
|
||||
|
||||
insert_tts_model_from_download_job(InsertTtsModelFromDownloadJobArgs {
|
||||
tts_model_type: TtsModelType::Tacotron2,
|
||||
title: model_title,
|
||||
original_download_url: "https://example.com",
|
||||
original_filename: filename,
|
||||
file_size_bytes: 0,
|
||||
creator_user_token: user_token,
|
||||
creator_ip_address: "127.0.0.1",
|
||||
creator_set_visibility: Visibility::Public,
|
||||
private_bucket_hash: &private_bucket_hash,
|
||||
private_bucket_object_name: &private_bucket_object_name,
|
||||
maybe_model_token: Some(&tts_model_token),
|
||||
mysql_pool,
|
||||
}).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn tts_model_exists(
|
||||
mysql_pool: &Pool<MySql>,
|
||||
tts_model_token: &TtsModelToken,
|
||||
) -> AnyhowResult<bool> {
|
||||
|
||||
const CAN_SEE_DELETED : bool = true;
|
||||
|
||||
let maybe_model = get_tts_model_by_token(
|
||||
tts_model_token.as_str(),
|
||||
CAN_SEE_DELETED,
|
||||
mysql_pool
|
||||
).await?;
|
||||
|
||||
Ok(maybe_model.is_some())
|
||||
}
|
||||
|
||||
struct BucketDetails {
|
||||
bucket_hash: String,
|
||||
bucket_object_name: String,
|
||||
}
|
||||
|
||||
async fn seed_file_to_bucket(
|
||||
model_file_path: &Path,
|
||||
bucket_clients: &BucketClients,
|
||||
bucket_path_unifier: &BucketPathUnifier
|
||||
) -> AnyhowResult<BucketDetails> {
|
||||
|
||||
info!("Uploading Tacotron2 model file {:?} ...", model_file_path);
|
||||
|
||||
// TODO(bt,2023-11-27): DO NOT DO THIS OR COPY THIS PATTERN
|
||||
// This method of uploading model files is super deprecated and results in inconsistent pathing.
|
||||
// Try to standardize on something resembling media_files going forward.
|
||||
let private_bucket_hash = sha256_hash_file(&model_file_path)?;
|
||||
|
||||
info!("File hash: {}", private_bucket_hash);
|
||||
|
||||
// FIXME(bt,2023-11-27): DO NOT COPY THIS PATTERN!
|
||||
// 'bucket_path_unifier' is super deprecated. Do not use it anymore or for anything except TT2!
|
||||
let model_bucket_path = bucket_path_unifier.tts_synthesizer_path(&private_bucket_hash);
|
||||
|
||||
//let public_upload_path = MediaFileBucketPath::generate_new(
|
||||
// Some("sample_"),
|
||||
// Some(".bin"));
|
||||
|
||||
let model_bucket_path = path_to_string(model_bucket_path);
|
||||
|
||||
info!("Uploading TT2 model to bucket path: {:?}", model_bucket_path);
|
||||
|
||||
info!("Reading TT2 model file: {:?}", model_file_path);
|
||||
|
||||
let bytes = file_read_bytes(model_file_path)?;
|
||||
let mimetype = get_mimetype_for_bytes(&bytes).unwrap_or("application/octet-stream");
|
||||
|
||||
let _r = bucket_clients.private.upload_file_with_content_type(
|
||||
&model_bucket_path,
|
||||
bytes.as_ref(),
|
||||
mimetype)
|
||||
.await?;
|
||||
|
||||
Ok(BucketDetails {
|
||||
bucket_hash: private_bucket_hash,
|
||||
bucket_object_name: model_bucket_path,
|
||||
})
|
||||
}
|
||||
@@ -5,25 +5,35 @@ use errors::{anyhow, AnyhowResult};
|
||||
use hashing::bcrypt::bcrypt_password_hash::bcrypt_password_hash;
|
||||
use hashing::md5::email_to_gravatar_hash::email_to_gravatar_hash;
|
||||
use mysql_queries::queries::users::user::create_account::{create_account, CreateAccountArgs};
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
pub const ADMIN_USERNAME : &str = "admin";
|
||||
pub const HANASHI_USERNAME : &str = "hanashi";
|
||||
|
||||
// We want deterministic tokens for some records
|
||||
pub const ADMIN_USER_TOKEN : &str = "user_4jgh4dk9tmr6t";
|
||||
pub const HANASHI_USER_TOKEN : &str = "user_fcfteany0ds60";
|
||||
|
||||
pub async fn seed_user_accounts(mysql_pool: &Pool<MySql>) -> AnyhowResult<()> {
|
||||
info!("Seeding user accounts...");
|
||||
|
||||
// NB: This is idempotent and will only install the accounts once.
|
||||
let users = [
|
||||
(ADMIN_USERNAME, "password"),
|
||||
(HANASHI_USERNAME, "password"),
|
||||
("test", "password"),
|
||||
(ADMIN_USERNAME, "password", Some(ADMIN_USER_TOKEN)),
|
||||
(HANASHI_USERNAME, "password", Some(HANASHI_USER_TOKEN)),
|
||||
("test", "password", None),
|
||||
];
|
||||
|
||||
for (username, password) in users {
|
||||
let result = seed_user(username, password, &mysql_pool).await;
|
||||
for (username, password, maybe_user_token) in users {
|
||||
let maybe_user_token = maybe_user_token.map(|token| UserToken::new_from_str(token));
|
||||
let result = seed_user(username, password, maybe_user_token, &mysql_pool).await;
|
||||
match result {
|
||||
Ok(_) => info!("Seeded {}", username),
|
||||
Err(err) => warn!("Could not seed user {} : {:?}", username, err),
|
||||
Err(err) => warn!(r#"
|
||||
Could not seed user {} : {:?}
|
||||
(No worries: if there's a duplicate key error, we probably already
|
||||
seeded the user on a previous invocation!)
|
||||
"#, username, err),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +43,7 @@ pub async fn seed_user_accounts(mysql_pool: &Pool<MySql>) -> AnyhowResult<()> {
|
||||
async fn seed_user(
|
||||
username: &str,
|
||||
password: &str,
|
||||
maybe_user_token: Option<UserToken>,
|
||||
mysql_pool: &Pool<MySql>,
|
||||
) -> AnyhowResult<()> {
|
||||
info!("Seeding user {} ...", username);
|
||||
@@ -50,6 +61,7 @@ async fn seed_user(
|
||||
email_gravatar_hash: &email_gravatar_hash,
|
||||
password_hash: &password_hash,
|
||||
ip_address: "127.0.0.1",
|
||||
maybe_user_token: maybe_user_token.as_ref(),
|
||||
}).await.map_err(|err| anyhow!("err: {:?}", err))?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -22,9 +22,10 @@ use tokens::tokens::users::UserToken;
|
||||
use tokens::tokens::zs_voice_datasets::ZsVoiceDatasetToken;
|
||||
use tokens::tokens::zs_voices::ZsVoiceToken;
|
||||
|
||||
use crate::bucket_clients::BucketClients;
|
||||
use crate::seeding::users::HANASHI_USERNAME;
|
||||
|
||||
pub async fn seed_zero_shot_tts(mysql_pool: &Pool<MySql>, maybe_bucket_client: Option<&BucketClient>) -> AnyhowResult<()> {
|
||||
pub async fn seed_zero_shot_tts(mysql_pool: &Pool<MySql>, maybe_bucket_clients: Option<&BucketClients>) -> AnyhowResult<()> {
|
||||
info!("Seeding zero shot TTS...");
|
||||
|
||||
let user_token = match get_user_token_by_username(HANASHI_USERNAME, mysql_pool).await? {
|
||||
@@ -46,7 +47,7 @@ pub async fn seed_zero_shot_tts(mysql_pool: &Pool<MySql>, maybe_bucket_client: O
|
||||
];
|
||||
|
||||
for (voice_name, bucket_hash, wav_file, user_token) in records {
|
||||
create_voice_records(voice_name, bucket_hash, wav_file, user_token, mysql_pool, maybe_bucket_client).await?;
|
||||
create_voice_records(voice_name, bucket_hash, wav_file, user_token, mysql_pool, maybe_bucket_clients).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -58,7 +59,7 @@ async fn create_voice_records(
|
||||
wav_file: &str,
|
||||
creator_user_token: &UserToken,
|
||||
mysql_pool: &Pool<MySql>,
|
||||
maybe_bucket_client: Option<&BucketClient>,
|
||||
maybe_bucket_clients: Option<&BucketClients>,
|
||||
) -> AnyhowResult<(ZsVoiceToken, ZsVoiceDatasetToken)> {
|
||||
info!("Creating voice records for voice {} ...", voice_name);
|
||||
|
||||
@@ -76,8 +77,8 @@ async fn create_voice_records(
|
||||
|
||||
let public_upload_path;
|
||||
|
||||
if let Some(bucket_client) = maybe_bucket_client {
|
||||
public_upload_path = seed_file_to_bucket(wav_file, bucket_client).await?;
|
||||
if let Some(bucket_clients) = maybe_bucket_clients {
|
||||
public_upload_path = seed_file_to_bucket(wav_file, &bucket_clients.public).await?;
|
||||
} else {
|
||||
public_upload_path = MediaFileBucketPath::from_object_hash("fake", None, None);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use anyhow::anyhow;
|
||||
use log::warn;
|
||||
use sqlx::MySqlPool;
|
||||
use sqlx::error::Error::Database;
|
||||
use sqlx::mysql::MySqlQueryResult;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use reusable_types::achievements::user_badge::UserBadge;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::mediators::firehose_publisher::FirehosePublisher;
|
||||
|
||||
@@ -34,8 +35,8 @@ impl BadgeGranter {
|
||||
}
|
||||
|
||||
/// This needs to be called *after* successful upload.
|
||||
pub async fn maybe_grant_tts_model_uploads_badge(&self, user_token: &str) -> AnyhowResult<()> {
|
||||
let count = self.count_tts_models_uploaded(user_token).await?;
|
||||
pub async fn maybe_grant_tts_model_uploads_badge(&self, user_token: &UserToken) -> AnyhowResult<()> {
|
||||
let count = self.count_tts_models_uploaded(user_token.as_str()).await?;
|
||||
|
||||
let mut maybe_badge = None;
|
||||
|
||||
@@ -68,16 +69,16 @@ impl BadgeGranter {
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
if self.has_badge(user_token, badge).await? {
|
||||
if self.has_badge(user_token.as_str(), badge).await? {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let _record_id = self.insert(
|
||||
badge,
|
||||
user_token,
|
||||
user_token.as_str(),
|
||||
).await?;
|
||||
|
||||
self.firehose_publisher.publish_user_badge_granted(user_token, badge.to_db_value())
|
||||
self.firehose_publisher.publish_user_badge_granted(user_token.as_str(), badge.to_db_value())
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
+4
-3
@@ -8,6 +8,7 @@ use enums::common::job_status::JobStatus;
|
||||
use enums::common::visibility::Visibility;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::generic_download_jobs::DownloadJobToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::queries::generic_download::job::_keys::GenericDownloadJobId;
|
||||
|
||||
@@ -17,7 +18,7 @@ pub struct AvailableDownloadJob {
|
||||
pub id: GenericDownloadJobId,
|
||||
pub download_job_token: DownloadJobToken,
|
||||
|
||||
pub creator_user_token: String,
|
||||
pub creator_user_token: UserToken,
|
||||
pub creator_ip_address: String,
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
@@ -51,7 +52,7 @@ SELECT
|
||||
id,
|
||||
token,
|
||||
|
||||
creator_user_token,
|
||||
creator_user_token as `creator_user_token: tokens::tokens::users::UserToken`,
|
||||
creator_ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
@@ -143,7 +144,7 @@ struct AvailableDownloadJobRawInternal {
|
||||
pub id: i64,
|
||||
pub token: String,
|
||||
|
||||
pub creator_user_token: String,
|
||||
pub creator_user_token: UserToken,
|
||||
pub creator_ip_address: String,
|
||||
pub creator_set_visibility: String,
|
||||
|
||||
|
||||
+12
-3
@@ -7,6 +7,7 @@ use enums::by_table::tts_models::tts_model_type::TtsModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::tts_models::TtsModelToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
pub struct InsertTtsModelFromDownloadJobArgs<'a, P: AsRef<Path>> {
|
||||
pub tts_model_type: TtsModelType,
|
||||
@@ -17,22 +18,30 @@ pub struct InsertTtsModelFromDownloadJobArgs<'a, P: AsRef<Path>> {
|
||||
pub original_filename: &'a str,
|
||||
pub file_size_bytes: u64,
|
||||
|
||||
pub creator_user_token: &'a str,
|
||||
pub creator_user_token: &'a UserToken,
|
||||
pub creator_ip_address: &'a str,
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub private_bucket_hash: &'a str,
|
||||
pub private_bucket_object_name: P,
|
||||
|
||||
/// In production code, send this as `None`.
|
||||
/// Only provide an external model token for db integration tests and db seeding tools.
|
||||
/// This allows for knowing the model token a priori.
|
||||
pub maybe_model_token: Option<&'a TtsModelToken>,
|
||||
|
||||
pub mysql_pool: &'a MySqlPool,
|
||||
}
|
||||
|
||||
|
||||
pub async fn insert_tts_model_from_download_job<P: AsRef<Path>>(
|
||||
args: InsertTtsModelFromDownloadJobArgs<'_, P>,
|
||||
) -> AnyhowResult<(u64, String)> {
|
||||
) -> AnyhowResult<(u64, TtsModelToken)> {
|
||||
|
||||
let model_token = TtsModelToken::generate().to_string();
|
||||
let model_token = match args.maybe_model_token {
|
||||
None => TtsModelToken::generate(),
|
||||
Some(model_token) => model_token.clone(),
|
||||
};
|
||||
|
||||
let private_bucket_object_name = &args.private_bucket_object_name
|
||||
.as_ref()
|
||||
|
||||
@@ -16,6 +16,11 @@ pub struct CreateAccountArgs<'a> {
|
||||
pub email_gravatar_hash: &'a str,
|
||||
pub password_hash: &'a str,
|
||||
pub ip_address: &'a str,
|
||||
|
||||
/// In production code, send this as `None`.
|
||||
/// Only provide an external user token for db integration tests and db seeding tools.
|
||||
/// This allows for knowing the user token a priori.
|
||||
pub maybe_user_token: Option<&'a UserToken>,
|
||||
}
|
||||
|
||||
pub struct CreateAccountSuccessResult {
|
||||
@@ -40,7 +45,10 @@ pub async fn create_account(
|
||||
const INITIAL_PROFILE_RENDERED_HTML : &str = "";
|
||||
const INITIAL_USER_ROLE: &str = "user";
|
||||
|
||||
let user_token = UserToken::generate();
|
||||
let user_token = match args.maybe_user_token {
|
||||
None => UserToken::generate(),
|
||||
Some(user_token) => user_token.clone(),
|
||||
};
|
||||
|
||||
let query_result = sqlx::query!(
|
||||
r#"
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
mod paths;
|
||||
mod substituted_path;
|
||||
|
||||
pub use paths::seed_tool_data_root::get_seed_tool_data_root;
|
||||
pub use paths::storyteller_frontend_root::get_storyteller_frontend_root;
|
||||
pub use paths::storyteller_ml_root::get_storyteller_ml_root;
|
||||
pub use paths::storyteller_root::get_storyteller_root;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub (crate) mod env_get_path;
|
||||
pub (crate) mod seed_tool_data_root;
|
||||
pub (crate) mod storyteller_frontend_root;
|
||||
pub (crate) mod storyteller_ml_root;
|
||||
pub (crate) mod storyteller_root;
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::get_storyteller_root;
|
||||
use crate::paths::env_get_path::env_get_path;
|
||||
|
||||
/// The env var that declares where the seed tool data repo lives.
|
||||
pub const SEED_TOOL_DATA_ROOT : &str = "SEED_TOOL_DATA_ROOT";
|
||||
|
||||
// DO NOT LEAK THIS. THIS IS FOR TESTING ONLY.
|
||||
pub (crate) const TEST_SEED_TOOL_DATA_ROOT : &str = "ENV_TEST_SEED_TOOL_DATA_ROOT_DO_NOT_LEAK_";
|
||||
|
||||
/// Get the root of the seed-tool-data repo
|
||||
pub fn get_seed_tool_data_root() -> PathBuf {
|
||||
// 1) Try "STORYTELLER_RUST_ROOT" env var override. Do not check for path existence.
|
||||
let root = if cfg!(test) { TEST_SEED_TOOL_DATA_ROOT } else { SEED_TOOL_DATA_ROOT };
|
||||
|
||||
if let Ok(Some(path)) = env_get_path(root) {
|
||||
return path;
|
||||
}
|
||||
|
||||
// 2) Relative to "storyteller root"
|
||||
let mut dir = get_storyteller_root();
|
||||
dir.push("seed-tool-data");
|
||||
dir
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::env::{current_dir, temp_dir};
|
||||
use std::fs::{create_dir, remove_dir};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serial_test::serial;
|
||||
|
||||
use crate::paths::storyteller_root::TEST_HOME;
|
||||
use crate::paths::seed_tool_data_root::{get_seed_tool_data_root, TEST_SEED_TOOL_DATA_ROOT};
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_seed_tool_data_home_env_var() {
|
||||
// NB: Careful with `set_var` as it escapes into *all* other tests! To make matters
|
||||
// more complicated, tests run in parallel and in any order. We're using a special
|
||||
// test variable so we never infect other tests.
|
||||
std::env::set_var(TEST_SEED_TOOL_DATA_ROOT, "/testing/storyteller/root/custom-seed-tool-data");
|
||||
assert_eq!(get_seed_tool_data_root(), PathBuf::from("/testing/storyteller/root/custom-seed-tool-data"));
|
||||
std::env::remove_var(TEST_SEED_TOOL_DATA_ROOT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_relative_to_home_directory_no_directories_exist() {
|
||||
// NB: Careful with `set_var` as it escapes into *all* other tests! To make matters
|
||||
// more complicated, tests run in parallel and in any order. We're using a special
|
||||
// test variable so we never infect other tests.
|
||||
std::env::set_var(TEST_HOME, "/testing/fake_home");
|
||||
assert_eq!(get_seed_tool_data_root(), PathBuf::from("/testing/fake_home/code/storyteller/seed-tool-data"));
|
||||
std::env::remove_var(TEST_HOME);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_relative_to_home_directory_with_existing_directory() {
|
||||
let mut fake_home = temp_dir();
|
||||
fake_home.push("temp_dir_for_testing");
|
||||
|
||||
let mut fake_dev = fake_home.clone();
|
||||
fake_dev.push("dev");
|
||||
|
||||
// NB: Final path should be /tmp/temp_dir_for_testing/dev/storyteller
|
||||
let mut fake_storyteller = fake_dev.clone();
|
||||
fake_storyteller.push("storyteller");
|
||||
|
||||
// NB: Ordered for repeatability
|
||||
let _r = remove_dir(&fake_home).ok();
|
||||
let _r = create_dir(&fake_home).ok();
|
||||
let _r = create_dir(&fake_dev).ok();
|
||||
let _r = create_dir(&fake_storyteller).ok();
|
||||
|
||||
// NB: Careful with `set_var` as it escapes into *all* other tests! To make matters
|
||||
// more complicated, tests run in parallel and in any order. We're using a special
|
||||
// test variable so we never infect other tests.
|
||||
std::env::set_var(TEST_HOME, fake_home.to_str().expect("should be string"));
|
||||
|
||||
let mut expected = fake_storyteller.clone();
|
||||
expected.push("seed-tool-data");
|
||||
|
||||
let actual = get_seed_tool_data_root();
|
||||
|
||||
if actual.starts_with("/private") && !expected.starts_with("/private") {
|
||||
// NB: For Mac, TempDir creates in /var/... which is an alias of /private/var/...
|
||||
// We'll make sure to canonicalize both paths.
|
||||
let mut corrected = PathBuf::from("/private");
|
||||
|
||||
expected.iter()
|
||||
.filter(|component| !component.to_string_lossy().eq("/"))
|
||||
.for_each(|component| corrected.push(component));
|
||||
|
||||
expected = corrected;
|
||||
}
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
std::env::remove_var(TEST_HOME);
|
||||
|
||||
// NB: Must remove in reverse order, or we leave dangling pointers
|
||||
let _r = remove_dir(&fake_storyteller).ok();
|
||||
let _r = remove_dir(&fake_dev).ok();
|
||||
let _r = remove_dir(&fake_home).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_relative_to_current_working_directory() {
|
||||
// NB: In this test we have no "TEST_HOME" directory.
|
||||
let current_working_dir = current_dir().expect("Should have working dir.");
|
||||
|
||||
let mut expected_dir = current_working_dir.clone();
|
||||
expected_dir.push("code/storyteller/seed-tool-data");
|
||||
|
||||
assert_eq!(get_seed_tool_data_root(), expected_dir);
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -76,12 +76,15 @@ pub async fn process_tacotron_model<'a, 'b>(
|
||||
|
||||
info!("File hash: {}", private_bucket_hash);
|
||||
|
||||
// FIXME(bt,2023-11-27): 'bucket_path_unifier' is super deprecated. Do not use it anymore or for anything except TT2!
|
||||
let model_bucket_path = job_state.bucket_path_unifier.tts_synthesizer_path(&private_bucket_hash);
|
||||
|
||||
info!("Destination bucket path: {:?}", &model_bucket_path);
|
||||
|
||||
redis_logger.log_status("uploading tacotron TTS model")?;
|
||||
|
||||
// TODO(bt,2023-11-27): This method of uploading model files is super deprecated.
|
||||
// Try to standardize on something resembling media_files going forward.
|
||||
if let Err(e) = job_state.bucket_client.upload_filename(&model_bucket_path, &file_path).await {
|
||||
safe_delete_temp_file(&output_metadata_fs_path);
|
||||
safe_delete_temp_file(&file_path);
|
||||
@@ -111,6 +114,7 @@ pub async fn process_tacotron_model<'a, 'b>(
|
||||
creator_set_visibility: Visibility::Public, // TODO: All models default to public at start
|
||||
private_bucket_hash: &private_bucket_hash,
|
||||
private_bucket_object_name: &model_bucket_path,
|
||||
maybe_model_token: None, // NB: This parameter is for internal testing only
|
||||
mysql_pool: &job_state.mysql_pool,
|
||||
}).await?;
|
||||
|
||||
@@ -122,7 +126,7 @@ pub async fn process_tacotron_model<'a, 'b>(
|
||||
})?;
|
||||
|
||||
Ok(JobResults {
|
||||
entity_token: Some(model_token),
|
||||
entity_token: Some(model_token.to_string()),
|
||||
entity_type: Some(TtsModelType::Tacotron2.to_string()), // NB: This may be different from `GenericDownloadType` in the future!
|
||||
})
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ pub async fn process_vits_model<'a, 'b>(
|
||||
creator_set_visibility: Visibility::Public, // TODO: All models default to public at start
|
||||
private_bucket_hash: &private_bucket_hash,
|
||||
private_bucket_object_name: "", // TODO: This should go away.
|
||||
maybe_model_token: None, // NB: This parameter is for internal testing only
|
||||
mysql_pool: &job_state.mysql_pool,
|
||||
}).await?;
|
||||
|
||||
@@ -155,7 +156,7 @@ pub async fn process_vits_model<'a, 'b>(
|
||||
})?;
|
||||
|
||||
Ok(JobResults {
|
||||
entity_token: Some(model_token),
|
||||
entity_token: Some(model_token.to_string()),
|
||||
entity_type: Some(TtsModelType::Vits.to_string()), // NB: This may be different from `GenericDownloadType` in the future!
|
||||
})
|
||||
}
|
||||
@@ -160,6 +160,7 @@ pub async fn create_account_handler(
|
||||
email_gravatar_hash: &email_gravatar_hash,
|
||||
password_hash: &password_hash,
|
||||
ip_address: &ip_address,
|
||||
maybe_user_token: None, // NB: This parameter is for internal testing only
|
||||
}
|
||||
).await;
|
||||
|
||||
|
||||
Reference in new issue
Block a user