mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-22 01:29:31 -04:00
Compare commits
284
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a5e4538a0 | ||
|
|
3f70ca72dd | ||
|
|
d8a4b6c735 | ||
|
|
48ed6d118c | ||
|
|
4c3ac3edda | ||
|
|
dae5764658 | ||
|
|
98547df063 | ||
|
|
9c856235fc | ||
|
|
205f6de1b2 | ||
|
|
a9fa45575e | ||
|
|
1b77974a0e | ||
|
|
724ae2570e | ||
|
|
dc7b5b7e60 | ||
|
|
3259433717 | ||
|
|
bc5d84a306 | ||
|
|
2357f00944 | ||
|
|
24127f20e4 | ||
|
|
4c8f14d4d5 | ||
|
|
e14440ba02 | ||
|
|
7295a3295f | ||
|
|
10176079c7 | ||
|
|
8999a80e92 | ||
|
|
41b07049ff | ||
|
|
b45f6ed691 | ||
|
|
9d16eb05c1 | ||
|
|
b414540251 | ||
|
|
2949456adc | ||
|
|
3a72292898 | ||
|
|
07c640ce84 | ||
|
|
49a15fd1f1 | ||
|
|
3267f9949d | ||
|
|
47df6930d2 | ||
|
|
913d282d60 | ||
|
|
743934ff50 | ||
|
|
bc209fe939 | ||
|
|
302975ddfd | ||
|
|
d7664e8e1c | ||
|
|
9f7fc09e9f | ||
|
|
2686cb94e0 | ||
|
|
fd7f1e1893 | ||
|
|
eb3499596e | ||
|
|
00bfdad3d0 | ||
|
|
ff3821956a | ||
|
|
a5a5f5473b | ||
|
|
7c553ffcee | ||
|
|
52f0b44c26 | ||
|
|
fbed7cd530 | ||
|
|
4ce8752a6a | ||
|
|
3239e318b1 | ||
|
|
471dcff697 | ||
|
|
2af57fcf3e | ||
|
|
f2b0e77210 | ||
|
|
10c8bc1ef3 | ||
|
|
35dfe6a7f2 | ||
|
|
48ecc44978 | ||
|
|
4f0e2d5f72 | ||
|
|
96c99fd102 | ||
|
|
26794b3d2e | ||
|
|
c3a60ed8ab | ||
|
|
13964901f0 | ||
|
|
593fb50fcb | ||
|
|
fff71d7aa4 | ||
|
|
57eecb3757 | ||
|
|
022aa523cb | ||
|
|
6d01506bd1 | ||
|
|
0cc3baee27 | ||
|
|
d5b28d2d3e | ||
|
|
d6a3d4887f | ||
|
|
cce4f5f3b7 | ||
|
|
9123dbc3d9 | ||
|
|
d43a747033 | ||
|
|
c3e5b42bf3 | ||
|
|
25cdbf2f1d | ||
|
|
59f7b6f732 | ||
|
|
fe7251744f | ||
|
|
fdd60b436a | ||
|
|
0836e88f4c | ||
|
|
261978f763 | ||
|
|
d072e021d1 | ||
|
|
66442d9eda | ||
|
|
0f4cab9ca3 | ||
|
|
6de4d451ac | ||
|
|
9d4e426ace | ||
|
|
bfb5103f46 | ||
|
|
3308c05836 | ||
|
|
5a539e589d | ||
|
|
604c72179a | ||
|
|
be19eefa1a | ||
|
|
34e2b1e544 | ||
|
|
95152e9a88 | ||
|
|
cbe8aaa1ba | ||
|
|
11185584a7 | ||
|
|
76559cf783 | ||
|
|
2e7bd622f3 | ||
|
|
68048659d4 | ||
|
|
608e94a6a8 | ||
|
|
9e0bee8fba | ||
|
|
dcec9b9e8a | ||
|
|
3fc0f45942 | ||
|
|
bfd325900f | ||
|
|
1ef7ae1009 | ||
|
|
17d51f8d87 | ||
|
|
ca81d1b543 | ||
|
|
1de3e669d6 | ||
|
|
ac39ac0a76 | ||
|
|
f9caa7d836 | ||
|
|
5f7a10a54c | ||
|
|
e478c73ddf | ||
|
|
92676e842c | ||
|
|
7eee225296 | ||
|
|
1319471c04 | ||
|
|
47e7269880 | ||
|
|
9b83caece1 | ||
|
|
138057e105 | ||
|
|
3e8b6f39fe | ||
|
|
63b4a3180f | ||
|
|
6842794af6 | ||
|
|
a963e5146a | ||
|
|
eb65ed5fa8 | ||
|
|
f6989230be | ||
|
|
2d3f29139a | ||
|
|
a059aa7271 | ||
|
|
8699a9e342 | ||
|
|
48bf168d99 | ||
|
|
f1fd99be15 | ||
|
|
a0b8849806 | ||
|
|
49db299f96 | ||
|
|
b1e87b7d90 | ||
|
|
bb0a4958ef | ||
|
|
4aacc23101 | ||
|
|
536307c162 | ||
|
|
d5c86d1345 | ||
|
|
1900a31541 | ||
|
|
067538f1c2 | ||
|
|
a256681f6d | ||
|
|
03443be19c | ||
|
|
ec03420efc | ||
|
|
bfc8171fa7 | ||
|
|
248ee45d11 | ||
|
|
97be5da1b5 | ||
|
|
d26902911f | ||
|
|
0560a2d607 | ||
|
|
264058ea87 | ||
|
|
b175cc0e65 | ||
|
|
ae781a3e37 | ||
|
|
ee00fbe585 | ||
|
|
82791ec39e | ||
|
|
b6bbc76b18 | ||
|
|
1742827ed9 | ||
|
|
68bc9d0bb0 | ||
|
|
ccc67d43cc | ||
|
|
3845c632b2 | ||
|
|
22518d9164 | ||
|
|
5dbed7afbb | ||
|
|
a485666b1c | ||
|
|
54e9b8af70 | ||
|
|
5a7b0b1bdd | ||
|
|
ee0067161e | ||
|
|
a0f464e454 | ||
|
|
332982faa5 | ||
|
|
c0e142d668 | ||
|
|
ca8dce4805 | ||
|
|
ed1eb9a540 | ||
|
|
0620fa6f98 | ||
|
|
94adc01d62 | ||
|
|
6e76cd3da6 | ||
|
|
5a1fd2679c | ||
|
|
f5c3e12f90 | ||
|
|
0a141ea118 | ||
|
|
dd6c5d1975 | ||
|
|
91dcd1f876 | ||
|
|
1869ead6ad | ||
|
|
1c81874b28 | ||
|
|
c8b79bfb40 | ||
|
|
767d76f831 | ||
|
|
b163791db2 | ||
|
|
d4e8bfb1f8 | ||
|
|
fabc1583ca | ||
|
|
804ab3e4e8 | ||
|
|
cd0e104808 | ||
|
|
a7d794e29b | ||
|
|
eed7dc28f8 | ||
|
|
d4c5710513 | ||
|
|
d8c4df4c02 | ||
|
|
13809ad309 | ||
|
|
95888e683d | ||
|
|
3012dea984 | ||
|
|
ae899d8aca | ||
|
|
dda05ba22c | ||
|
|
63795b2cb6 | ||
|
|
8aa97a6a4f | ||
|
|
e23369761b | ||
|
|
2b27d6fe41 | ||
|
|
c6eefc41ec | ||
|
|
9e3a3b79f9 | ||
|
|
2aa063c354 | ||
|
|
f77d4a636b | ||
|
|
018b05a3a2 | ||
|
|
fd3661e25d | ||
|
|
39de5aba13 | ||
|
|
6354e7c3b4 | ||
|
|
eaa9ddd62d | ||
|
|
195a2eeb9c | ||
|
|
bef5dce947 | ||
|
|
dd3f9c9d3e | ||
|
|
aac31f77ad | ||
|
|
d07a147db9 | ||
|
|
ba76acec35 | ||
|
|
a2c196587f | ||
|
|
8d36d6c709 | ||
|
|
4f5f6cfe51 | ||
|
|
7c59f267fc | ||
|
|
23a6daa144 | ||
|
|
a22038ea31 | ||
|
|
5de40b2dab | ||
|
|
484e6788e3 | ||
|
|
b67cfb5fbe | ||
|
|
d46075a2a3 | ||
|
|
aa9b834f4b | ||
|
|
2abba4b639 | ||
|
|
c42c091b2d | ||
|
|
cde8447a9c | ||
|
|
4306a4763d | ||
|
|
9701e29b58 | ||
|
|
8512846efa | ||
|
|
c7b54e1548 | ||
|
|
8343cf1b65 | ||
|
|
4d4b55d39d | ||
|
|
6b8c1a3106 | ||
|
|
63b3cb8216 | ||
|
|
3d4522126e | ||
|
|
8be1b5afa3 | ||
|
|
11f6995942 | ||
|
|
589d13fdd1 | ||
|
|
3f9160701f | ||
|
|
9e8c27edfd | ||
|
|
6540aed9dc | ||
|
|
7cd570083c | ||
|
|
8787e3ef1e | ||
|
|
49b25cc79d | ||
|
|
eff73a9cff | ||
|
|
d0e3e0f6bb | ||
|
|
e676df0bf4 | ||
|
|
ebed726768 | ||
|
|
e0145f0e70 | ||
|
|
2f3037514f | ||
|
|
f7c636cf58 | ||
|
|
ec4a172929 | ||
|
|
773b96fb35 | ||
|
|
40d0d0d844 | ||
|
|
dbae9803e2 | ||
|
|
87093bf291 | ||
|
|
0b3ea3af2a | ||
|
|
1a75d5b7b3 | ||
|
|
fb5dbae029 | ||
|
|
d2fba4349d | ||
|
|
b76b4460e3 | ||
|
|
5fee53d7c7 | ||
|
|
4f589cd1b7 | ||
|
|
5b9f1551ee | ||
|
|
e47125f12d | ||
|
|
afa050b45e | ||
|
|
ab6986e4b4 | ||
|
|
53626cb312 | ||
|
|
a8810a2db8 | ||
|
|
cb8266d61e | ||
|
|
056065458f | ||
|
|
7cb18d63f2 | ||
|
|
12a119cc1b | ||
|
|
c8d8546c57 | ||
|
|
3265e097c0 | ||
|
|
695d3da34b | ||
|
|
6733a19315 | ||
|
|
baefb9f7d6 | ||
|
|
fc47232e6b | ||
|
|
83dc262249 | ||
|
|
1d3885c367 | ||
|
|
070433b416 | ||
|
|
f15b89cf3d | ||
|
|
699bb6a6a7 | ||
|
|
226f111a00 | ||
|
|
b7794ab48f | ||
|
|
0bd5c9e93b | ||
|
|
5a981f55aa |
@@ -75,3 +75,8 @@ TTS_HIFIGAN_SUPERRES_VOCODER_MODEL_FILENAME=hifigan_superres_twilight_33000.pt
|
||||
|
||||
# Development key. Permissible to check in.
|
||||
NEWRELIC_API_KEY=c402be1c4c4216ec83d0cfc0851520cfFFFFNRAL
|
||||
|
||||
STORYTELLER_ROOT = "/home/tensor/code/storyteller" # your path here!
|
||||
STORYTELLER_FRONTEND = "storyteller-frontend"
|
||||
STORYTELLER_ML = "storyteller-ml"
|
||||
STORYTELLER_RUST = "storyteller-rust"
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
# Ignore certain code for Github language statistics
|
||||
# https://github.com/github/linguist/blob/master/docs/overrides.md
|
||||
old_code/trumpet-trumped.com/dictionary/cmudict-0.7b linguist-vendored
|
||||
old_code/voder-vocodes-1.0/frontends/admin/node_modules linguist-vendored
|
||||
windows/sqlite linguist-vendored
|
||||
old_code/voder-vocodes-1.0/frontends/admin/node_modules/** linguist-vendored
|
||||
test_data/html_scraping/** linguist-vendored
|
||||
windows/sqlite/** linguist-vendored
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "cargo" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
Generated
+6
@@ -3,6 +3,8 @@
|
||||
<words>
|
||||
<w>arpabet</w>
|
||||
<w>backfill</w>
|
||||
<w>canonicalized</w>
|
||||
<w>canonicalizing</w>
|
||||
<w>cashapp</w>
|
||||
<w>cheermote</w>
|
||||
<w>cidrs</w>
|
||||
@@ -11,7 +13,9 @@
|
||||
<w>dalle</w>
|
||||
<w>demuxer</w>
|
||||
<w>dereferencing</w>
|
||||
<w>downloaders</w>
|
||||
<w>downvote</w>
|
||||
<w>encodec</w>
|
||||
<w>fakeyou</w>
|
||||
<w>ffprobe</w>
|
||||
<w>greenlight</w>
|
||||
@@ -22,6 +26,8 @@
|
||||
<w>hparams</w>
|
||||
<w>huggingface</w>
|
||||
<w>lipsync</w>
|
||||
<w>mochi</w>
|
||||
<w>nestedness</w>
|
||||
<w>pathbuf</w>
|
||||
<w>replayable</w>
|
||||
<w>retryably</w>
|
||||
|
||||
Generated
+606
-341
File diff suppressed because it is too large
Load Diff
+10
-3
@@ -11,28 +11,32 @@ members = [
|
||||
# CLI tools for operations, etc.
|
||||
"crates/cli/database_backup",
|
||||
"crates/cli/database_cleanup",
|
||||
"crates/cli/dev_database_seed",
|
||||
"crates/cli/dev_upload_media_file",
|
||||
"crates/cli/kubectl_pod_cleanup",
|
||||
"crates/cli/scrape_test",
|
||||
|
||||
# Shared libraries
|
||||
"crates/lib/actix_helpers",
|
||||
"crates/lib/api_clients/cloud_storage",
|
||||
#"crates/lib/api_clients/fakeyou_client",
|
||||
"crates/lib/api_clients/google_drive_common",
|
||||
"crates/lib/api_clients/twitch_common",
|
||||
"crates/lib/bootstrap",
|
||||
"crates/lib/caching/memory_caching",
|
||||
"crates/lib/caching/redis_caching",
|
||||
"crates/lib/concurrency",
|
||||
"crates/lib/cookies",
|
||||
"crates/lib/crockford",
|
||||
"crates/lib/datetimes",
|
||||
"crates/lib/deprecated/config", # TODO(bt, 2023-02-13): Deprecate or repurpose
|
||||
"crates/lib/deprecated/container_common", # TODO(bt, 2023-02-13): Pull apart and rehome pieces
|
||||
"crates/lib/deprecated/http_server_common", # TODO(bt, 2023-03-20): Move useful pieces to actix_helpers
|
||||
"crates/lib/envvar",
|
||||
"crates/lib/easyenv", # NB: Vendored version of https://github.com/echelon/easyenv.rs that we'll improve.
|
||||
"crates/lib/errors",
|
||||
"crates/lib/files/archives",
|
||||
"crates/lib/files/filesys",
|
||||
"crates/lib/files/hashing",
|
||||
"crates/lib/files/images",
|
||||
"crates/lib/files/media",
|
||||
"crates/lib/files/mimetypes",
|
||||
"crates/lib/idempotency",
|
||||
@@ -40,13 +44,16 @@ members = [
|
||||
"crates/lib/mysql_queries",
|
||||
"crates/lib/redis_common",
|
||||
"crates/lib/server_environment",
|
||||
#"crates/lib/sqlite_queries",
|
||||
"crates/lib/sqlx_mysql_helpers",
|
||||
"crates/lib/storyteller_root",
|
||||
"crates/lib/subprocess_common",
|
||||
"crates/lib/testing",
|
||||
"crates/lib/tts_common",
|
||||
"crates/lib/url_config",
|
||||
"crates/lib/user_input_common",
|
||||
"crates/lib/web_scrapers",
|
||||
#"crates/lib/api_clients/fakeyou_client",
|
||||
#"crates/lib/sqlite_queries",
|
||||
|
||||
# Programs: Desktop apps
|
||||
#"crates/programs/aichatbot-sidecar",
|
||||
|
||||
@@ -39,6 +39,9 @@ deny = [
|
||||
"clippy::redundant_field_names", # Lint: Duplicate names is a burden when reading: you wonder why they're dupes.
|
||||
"clippy::redundant_static_lifetimes", # Lint: feels more modern to elide static lifetimes
|
||||
|
||||
# Rust
|
||||
"unused_imports", # These are easy to automatically cull
|
||||
|
||||
# Possible:
|
||||
# "clippy::ptr_arg",
|
||||
# "clippy::too_many_lines" (note: this is configurable)
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
FakeYou Development Setup (bare metal)
|
||||
======================================
|
||||
|
||||
Set your Environment .env File with the following paths, copy this in
|
||||
The root path and the folders associated with the path.
|
||||
This will change the base paths for development.
|
||||
STORYTELLER_ROOT = "/home/tensor/code/storyteller" # change this
|
||||
STORYTELLER_FRONTEND = "storyteller-frontend"
|
||||
STORYTELLER_ML = "storyteller-ml"
|
||||
STORYTELLER_RUST = "storyteller-rust"
|
||||
|
||||
This document describes how to run the FakeYou infrastructure on your development
|
||||
machine's bare metal (no containers).
|
||||
|
||||
@@ -32,8 +40,7 @@ Mac: (Lost the list, but it should match Linux. Install Homebrew.)
|
||||
Ubuntu 22.04:
|
||||
|
||||
```bash
|
||||
sudo apt install \
|
||||
jq \
|
||||
sudo apt install jq \
|
||||
libmysqlclient-dev \
|
||||
libsqlite3-dev \
|
||||
pkgconf
|
||||
@@ -109,7 +116,9 @@ error (see below).
|
||||
### (7) (optional) Install the SQLx CLI (if doing database development)
|
||||
|
||||
```bash
|
||||
cargo install sqlx-cli --no-default-features --features rustls,mysql,sqlite
|
||||
# NB(bt,2023-10-04): "--no-default-features" not working
|
||||
# cargo install sqlx-cli --no-default-features --features rustls,mysql,sqlite
|
||||
cargo install sqlx-cli --features rustls,mysql,sqlite
|
||||
```
|
||||
|
||||
We'll be using diesel to manage the migrations, but sqlx within the app to actually perform queries.
|
||||
|
||||
@@ -10,4 +10,9 @@ select count(*) from generic_inference_jobs where status = 'pending';
|
||||
|
||||
update generic_inference_jobs
|
||||
set status = 'dead'
|
||||
where status IN ('pending', 'started', 'attempt_failed');
|
||||
where status IN ('pending', 'started', 'attempt_failed');
|
||||
|
||||
update generic_inference_jobs
|
||||
set status = 'dead'
|
||||
where status IN ('pending', 'started', 'attempt_failed')
|
||||
and maybe_model_type IN ('sad_talker', 'so_vits_svc');
|
||||
|
||||
@@ -302,7 +302,10 @@ where token IN (
|
||||
'vcm_p7m9jw1krrkr',
|
||||
'vcm_x7v3m9mcvten',
|
||||
'vcm_6r057m7594bv',
|
||||
'vcm_wkm9e42d3spm'
|
||||
'vcm_wkm9e42d3spm',
|
||||
'vcm_cvz78weydh57',
|
||||
'vcm_7g5q89m75n1p',
|
||||
'vcm_fg3175bxy7kj'
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- See how many jobs of each time were created in the last period
|
||||
select
|
||||
distinct maybe_model_type as model_type,
|
||||
count(*) as created
|
||||
from generic_inference_jobs
|
||||
where created_at > (CURDATE() - INTERVAL 60 MINUTE)
|
||||
group by maybe_model_type;
|
||||
|
||||
-- See how long recent jobs are taking
|
||||
select
|
||||
id,
|
||||
@@ -15,6 +23,19 @@ and success_execution_millis IS NOT NULL
|
||||
order by id desc
|
||||
limit 50;
|
||||
|
||||
-- See who is uploading SadTalker jobs
|
||||
select
|
||||
maybe_creator_user_token,
|
||||
users.username as maybe_creator_username,
|
||||
creator_ip_address,
|
||||
count(*) as attempts
|
||||
from generic_inference_jobs
|
||||
left outer join users
|
||||
on users.token = generic_inference_jobs.maybe_creator_user_token
|
||||
where maybe_model_type = 'sad_talker'
|
||||
and status IN ('pending', 'started', 'complete_failure', 'attempt_failed')
|
||||
group by maybe_creator_user_token, maybe_creator_username, creator_ip_address
|
||||
order by attempts desc;
|
||||
|
||||
-- Detailed report on most recent jobs, ordered by worst performing.
|
||||
-- TODO: Determine if the problem is in downloading models. Make sure the job timer doesn't include
|
||||
@@ -49,7 +70,8 @@ from (
|
||||
m.token = jobs.maybe_input_source_token
|
||||
where
|
||||
jobs.status != 'pending'
|
||||
AND jobs.created_at > NOW() - INTERVAL 20 MINUTE
|
||||
AND jobs.created_at > NOW() - INTERVAL 20 MINUTE
|
||||
AND jobs.maybe_model_type IN ('so_vits_svc')
|
||||
order by id desc
|
||||
limit 200
|
||||
) as t
|
||||
|
||||
@@ -35,7 +35,7 @@ order by attempts desc
|
||||
select *
|
||||
from tts_inference_jobs
|
||||
where status = 'pending'
|
||||
and creator_ip_address = '186.188.28.73'
|
||||
and creator_ip_address = '191.102.120.182'
|
||||
limit 1;
|
||||
|
||||
-- Top voices in requests
|
||||
|
||||
@@ -2,6 +2,20 @@
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- Per-user password resets.
|
||||
-- Password resets are valid until any of the following are true:
|
||||
--
|
||||
-- 1) database NOW() > password_rests.expires_at
|
||||
-- 2) password_rests.is_redeemed == TRUE
|
||||
-- 3) users.password_version > password_resets.current_password_version
|
||||
--
|
||||
-- When creating a new password reset, send the user an email with the `secret_key` in the URL.
|
||||
-- The user will have to supply their username and/or email address, plus the secret key to
|
||||
-- redeem the password reset.
|
||||
--
|
||||
-- Also when creating the password hash, be sure to set `expires_at` to be "NOW() + 3 hours", or
|
||||
-- some other small, reasonable time frame.
|
||||
--
|
||||
CREATE TABLE password_resets (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
@@ -22,7 +36,7 @@ CREATE TABLE password_resets (
|
||||
-- reset is no longer valid.
|
||||
current_password_version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- Username is a lookup key; display_name allows the user to add custom case.
|
||||
-- Whether the password reset has been consumed.
|
||||
is_redeemed BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
-- For abuse tracking.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
drop table zs_voices;
|
||||
@@ -0,0 +1,154 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- This table contains both zero shot TTS voices (speaker embeddings) and zero shot V2V voices (speaker embeddings).
|
||||
-- These are the zero shot voices that are usable by our users.
|
||||
CREATE TABLE zs_voices (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Effective "primary key" for joins, API, etc. (PUBLIC)
|
||||
token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- NB: No idempotency token.
|
||||
-- Store it in another table to prevent duplicate record creation from rapid user inputs.
|
||||
|
||||
-- ========== ZERO SHOT FOREIGN KEYS AND STATE==========
|
||||
|
||||
-- The zero shot dataset that was used to create this voice.
|
||||
-- The dataset will only be accessible to the original author of the voice.
|
||||
-- The original author can create multiple voices from the same dataset.
|
||||
dataset_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- When datasets are edited, we bump their version.
|
||||
-- This tells us which version of the dataset created this voice (not that we should preserve
|
||||
-- record of which samples are in which version.)
|
||||
-- We can surface this to the users as well.
|
||||
dataset_version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- ========== MODEL DETAILS ==========
|
||||
|
||||
-- The class or category of model/algorithm this voice is intended for.
|
||||
-- * 'tts' for zero shot TTS
|
||||
-- * 'vc' for zero shot voice conversion
|
||||
-- * 'pitch' for pitch correction models (hypothetical)
|
||||
-- * (Since this isn't an enum, the list of values can be expanded.)
|
||||
model_category VARCHAR(16) NOT NULL,
|
||||
|
||||
-- The model used for the voice:
|
||||
-- * 'vall-e-x' for Vall-E-X
|
||||
-- * 'ns2vc' for NaturalSpeech2 voice conversion (hypothetical)
|
||||
-- * (Since this isn't an enum, the list of values can be expanded.)
|
||||
model_type VARCHAR(16) NOT NULL,
|
||||
|
||||
-- The version of the model.
|
||||
-- If we deploy new model weights for the model, we may need to regenerate
|
||||
-- all of the corresponding speaker embeddings. This lets us know which voices
|
||||
-- are the old version.
|
||||
-- In some cases, we may need to preserve, hide, or roll back to older versions.
|
||||
model_version INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- What kind of features these are: spectrogram, encodec, etc.
|
||||
-- Technically we shouldn't need this, as the 2-tuple (model_type, model_version) implicitly knows
|
||||
-- what its features should be, but this is probably useful for our own analysis and may be easier
|
||||
-- to deal with.
|
||||
--
|
||||
-- Example values:
|
||||
-- * 'spectrogram'
|
||||
-- * 'encodec'
|
||||
-- * (Since this isn't an enum, the list of values can be expanded.)
|
||||
encoding_type VARCHAR(16) NOT NULL,
|
||||
|
||||
-- ========== VOICE DETAILS ==========
|
||||
|
||||
-- The "title" of the voice. This can be the name and additional information as supplied by the user.
|
||||
title VARCHAR(255) NOT NULL,
|
||||
|
||||
-- The full IETF BCP47 language tag (eg. en, en-US, es-419, ja-JP, pt, etc.)
|
||||
-- The wide varchar space is because these language tags can become cumbersome, eg "hy-Latn-IT-arevela"
|
||||
ietf_language_tag VARCHAR(64) NOT NULL DEFAULT 'en',
|
||||
|
||||
-- The IETF BCP47 language tag's primary language subtag (eg. "es-419" becomes "es" and "en-US" becomes "en")
|
||||
ietf_primary_language_subtag VARCHAR(12) NOT NULL DEFAULT 'en',
|
||||
|
||||
-- TODO: misc speaker metadata
|
||||
-- We'll want rich metadata for search
|
||||
-- series_name VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_name VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_gender VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_age VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_voice_style VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
-- ========== STORAGE DETAILS ==========
|
||||
|
||||
-- The hash for the bucket directory that contains the original upload
|
||||
-- eg. /speaker_vectors/{bucket_hash}/{voice_model_version}.bin
|
||||
bucket_hash VARCHAR(32) NOT NULL,
|
||||
|
||||
-- ========== CREATOR DETAILS AND PREFERENCES ==========
|
||||
|
||||
-- Foreign key to user
|
||||
maybe_creator_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- Based on a cookie sent by the frontend.
|
||||
-- We'll save this even if the user is logged in.
|
||||
maybe_creator_anonymous_visitor_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- For abuse tracking.
|
||||
-- Wide enough for IPv4/6
|
||||
creator_ip_address VARCHAR(40) NOT NULL,
|
||||
|
||||
-- The creator can set a desired visibility for their data.
|
||||
-- This does not always apply to every upload type.
|
||||
-- Additionally, some upload types may require moderator approval prior
|
||||
-- to being publicly listed, and this field has no bearing on that.
|
||||
-- NB: DO NOT CHANGE THE ORDER OF THE ENUM VALUES DURING SCHEMA MIGRATIONS.
|
||||
creator_set_visibility ENUM(
|
||||
'public',
|
||||
'hidden',
|
||||
'private'
|
||||
) NOT NULL DEFAULT 'public',
|
||||
|
||||
-- The synthetic id associated with this result.
|
||||
-- These ids are incremented on a per-user basis to help users
|
||||
-- sequence their own work. They serve no other purpose.
|
||||
maybe_creator_synthetic_id BIGINT(20) DEFAULT NULL,
|
||||
|
||||
-- ========== MODERATION DETAILS ==========
|
||||
|
||||
-- The last moderator that made changes last.
|
||||
-- Fine details of moderator actions are kept in a separate `audit_logs` table.
|
||||
maybe_mod_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- ========== TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- If this is removed by a mod or the creator.
|
||||
user_deleted_at TIMESTAMP NULL,
|
||||
mod_deleted_at TIMESTAMP NULL,
|
||||
|
||||
-- ========== VECTOR CLOCK ==========
|
||||
|
||||
-- Incremented with every update.
|
||||
version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- INDICES --
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (token),
|
||||
|
||||
-- TODO: These are not correct. Fix these before landing / running migrations.
|
||||
KEY index_model_category (model_category),
|
||||
KEY index_model_type (model_type),
|
||||
KEY index_model_type_and_version (model_type, model_version),
|
||||
KEY index_encoding_type (encoding_type),
|
||||
KEY index_ietf_primary_language_subtag (ietf_primary_language_subtag),
|
||||
KEY fk_maybe_creator_user_token (maybe_creator_user_token),
|
||||
KEY fk_maybe_mod_user_token (maybe_mod_user_token),
|
||||
KEY index_creator_ip_address (creator_ip_address),
|
||||
KEY index_user_deleted_at (user_deleted_at),
|
||||
KEY index_mod_deleted_at (mod_deleted_at)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
drop table zs_voice_datasets;
|
||||
@@ -0,0 +1,106 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- This table describes a zero shot voice in the process of being created (or later reused / edited / enhanced)
|
||||
-- These are not usable voices until their vector embeddings have been created, and this table does not serve
|
||||
-- the production voice list.
|
||||
--
|
||||
-- Multiple voices can be created from a single "dataset" as the data set is mutable over time. Users can add or
|
||||
-- remove new samples to the data set at any time.
|
||||
CREATE TABLE zs_voice_datasets (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Effective "primary key" for joins, API, etc. (PUBLIC)
|
||||
token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- NB: No idempotency token.
|
||||
-- Store it in another table to prevent duplicate record creation from rapid user inputs.
|
||||
|
||||
-- ========== VOICE DETAILS ==========
|
||||
|
||||
-- The "title" of the voice. This can be the name and additional information as supplied by the user.
|
||||
-- When the voice embeddings are created, this will be copied to the produced "voice" (embeddings) record.
|
||||
title VARCHAR(255) NOT NULL,
|
||||
|
||||
-- The full IETF BCP47 language tag (eg. en, en-US, es-419, ja-JP, pt, etc.)
|
||||
-- The wide varchar space is because these language tags can become cumbersome, eg "hy-Latn-IT-arevela"
|
||||
ietf_language_tag VARCHAR(64) NOT NULL DEFAULT 'en',
|
||||
|
||||
-- The IETF BCP47 language tag's primary language subtag (eg. "es-419" becomes "es" and "en-US" becomes "en")
|
||||
ietf_primary_language_subtag VARCHAR(12) NOT NULL DEFAULT 'en',
|
||||
|
||||
-- TODO: misc speaker metadata
|
||||
-- We'll want rich metadata for search
|
||||
-- When the voice embeddings are created, this will be copied to the produced "voice" (embeddings) record.
|
||||
-- We don't want a hard link between the dataset and the produced voice as the voice is static once
|
||||
-- created (with the exception of regenerating embeddings), but the dataset my drift as the user experiments,
|
||||
-- adds/removes samples, etc.
|
||||
-- series_name VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_name VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_gender VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_age VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_voice_style VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
-- ========== CREATOR DETAILS AND PREFERENCES ==========
|
||||
|
||||
-- Foreign key to user
|
||||
maybe_creator_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- Based on a cookie sent by the frontend.
|
||||
-- We'll save this even if the user is logged in.
|
||||
maybe_creator_anonymous_visitor_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- For abuse tracking.
|
||||
-- Wide enough for IPv4/6
|
||||
creator_ip_address VARCHAR(40) NOT NULL,
|
||||
|
||||
-- The creator can set a desired visibility for their data.
|
||||
-- This does not always apply to every upload type.
|
||||
-- Additionally, some upload types may require moderator approval prior
|
||||
-- to being publicly listed, and this field has no bearing on that.
|
||||
-- NB: DO NOT CHANGE THE ORDER OF THE ENUM VALUES DURING SCHEMA MIGRATIONS.
|
||||
creator_set_visibility ENUM(
|
||||
'public',
|
||||
'hidden',
|
||||
'private'
|
||||
) NOT NULL DEFAULT 'public',
|
||||
|
||||
-- The synthetic id associated with this result.
|
||||
-- These ids are incremented on a per-user basis to help users
|
||||
-- sequence their own work. They serve no other purpose.
|
||||
maybe_creator_synthetic_id BIGINT(20) DEFAULT NULL,
|
||||
|
||||
-- ========== MODERATION DETAILS ==========
|
||||
|
||||
-- The last moderator that made changes last.
|
||||
-- Fine details of moderator actions are kept in a separate `audit_logs` table.
|
||||
maybe_mod_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- ========== TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- If this is removed by a mod or the creator.
|
||||
user_deleted_at TIMESTAMP NULL,
|
||||
mod_deleted_at TIMESTAMP NULL,
|
||||
|
||||
-- ========== VECTOR CLOCK ==========
|
||||
|
||||
-- Incremented with every update.
|
||||
version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- INDICES --
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (token),
|
||||
|
||||
KEY index_ietf_primary_language_subtag (ietf_primary_language_subtag),
|
||||
KEY fk_maybe_creator_user_token (maybe_creator_user_token),
|
||||
KEY fk_maybe_mod_user_token (maybe_mod_user_token),
|
||||
KEY index_creator_ip_address (creator_ip_address),
|
||||
KEY index_user_deleted_at (user_deleted_at),
|
||||
KEY index_mod_deleted_at (mod_deleted_at)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
drop table zs_voice_dataset_samples;
|
||||
@@ -0,0 +1,84 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- These are the individual files in each dataset.
|
||||
-- Files can be added and removed from a dataset at any time.
|
||||
CREATE TABLE zs_voice_dataset_samples (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Effective "primary key" for joins, API, etc. (PUBLIC)
|
||||
token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- NB: No idempotency token.
|
||||
-- Store it in another table to prevent duplicate record creation from rapid user inputs.
|
||||
|
||||
-- ========== ZERO SHOT FOREIGN KEYS AND STATE==========
|
||||
|
||||
-- The zero shot dataset that was used to create this voice.
|
||||
-- The dataset will only be accessible to the original author of the voice.
|
||||
-- The original author can create multiple voices from the same dataset.
|
||||
dataset_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Foreign key to the uploaded media file.
|
||||
-- There was an option of handling uploads and file management separately, but by electing to store the dataset
|
||||
-- samples in the "media_files" table, they can be reused by our other code, queries, machinery, etc.
|
||||
-- Note that this requires that the "media_files" record must be an audio (not video) file (which our code will
|
||||
-- enforce), and that it is given a category of "voice_zero_shot" so it isn't co-mingled with other uploads/outputs
|
||||
media_file_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Some data sets require raw transcripts for the audio.
|
||||
-- If required, they'll live here.
|
||||
maybe_raw_transcription TEXT DEFAULT NULL,
|
||||
|
||||
-- A way to order the data set samples
|
||||
-- We don't necessarily need to reimplement file reordering.
|
||||
-- order_id INT DEFAULT NULL,
|
||||
|
||||
-- ========== CREATOR DETAILS AND PREFERENCES ==========
|
||||
|
||||
-- Foreign key to user
|
||||
maybe_creator_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- Based on a cookie sent by the frontend.
|
||||
-- We'll save this even if the user is logged in.
|
||||
maybe_creator_anonymous_visitor_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- For abuse tracking.
|
||||
-- Wide enough for IPv4/6
|
||||
creator_ip_address VARCHAR(40) NOT NULL,
|
||||
|
||||
-- ========== MODERATION DETAILS ==========
|
||||
|
||||
-- The last moderator that made changes last.
|
||||
-- Fine details of moderator actions are kept in a separate `audit_logs` table.
|
||||
maybe_mod_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- ========== TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- If this is removed by a mod or the creator.
|
||||
user_deleted_at TIMESTAMP NULL,
|
||||
mod_deleted_at TIMESTAMP NULL,
|
||||
|
||||
-- ========== VECTOR CLOCK ==========
|
||||
|
||||
-- Incremented with every update.
|
||||
version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- INDICES --
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (token),
|
||||
|
||||
KEY fk_dataset_token (dataset_token),
|
||||
KEY fk_media_file_token (media_file_token),
|
||||
KEY fk_maybe_creator_user_token (maybe_creator_user_token),
|
||||
KEY fk_maybe_mod_user_token (maybe_mod_user_token),
|
||||
KEY index_creator_ip_address (creator_ip_address),
|
||||
KEY index_user_deleted_at (user_deleted_at),
|
||||
KEY index_mod_deleted_at (mod_deleted_at)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
drop table zs_voice_included_samples;
|
||||
@@ -0,0 +1,26 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- In order to rebuild any "zs_voice", we need to know which dataset samples were included.
|
||||
-- Since users can change the dataset at any time after a voice was originally created, these
|
||||
-- records are generated to retain a record of which samples fed into a given voice.
|
||||
-- These records will probably not be used for any product features, just as a means to study
|
||||
-- poor-performing voices, perform migrations, etc.
|
||||
CREATE TABLE zs_voice_included_samples (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- First member of composite effective "primary key"
|
||||
-- The "zs_voice" that was created.
|
||||
voice_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Second member of composite effective "primary key"
|
||||
-- The "zs_voice_dataset_sample" that was used.
|
||||
dataset_sample_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- INDICES --
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (voice_token, dataset_sample_token)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files DROP COLUMN maybe_public_bucket_extension;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN maybe_public_bucket_extension VARCHAR(16) DEFAULT NULL
|
||||
AFTER public_bucket_directory_hash;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files DROP COLUMN maybe_public_bucket_prefix;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN maybe_public_bucket_prefix VARCHAR(16) DEFAULT NULL
|
||||
AFTER public_bucket_directory_hash;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files DROP COLUMN origin_product_category;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN origin_product_category VARCHAR(16) NOT NULL DEFAULT "unknown"
|
||||
AFTER origin_category;
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD INDEX index_origin_product_category (origin_product_category);
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE generic_inference_jobs DROP COLUMN frontend_failure_category;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE generic_inference_jobs
|
||||
ADD COLUMN frontend_failure_category VARCHAR(32) DEFAULT NULL
|
||||
AFTER internal_debugging_failure_reason;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE zs_voice_dataset_samples DROP COLUMN uuid_idempotency_token;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE zs_voice_dataset_samples
|
||||
ADD COLUMN uuid_idempotency_token VARCHAR(36) NOT NULL
|
||||
AFTER token;
|
||||
|
||||
ALTER TABLE zs_voice_dataset_samples
|
||||
ADD UNIQUE INDEX unique_index_uuid_idempotency_token (uuid_idempotency_token);
|
||||
@@ -0,0 +1,5 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE password_resets DROP INDEX unique_user_token_and_secret_key;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE password_resets
|
||||
ADD UNIQUE INDEX unique_user_token_and_secret_key (user_token, secret_key);
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE password_resets
|
||||
MODIFY ip_address_redemption VARCHAR(40) NOT NULL;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE password_resets
|
||||
MODIFY ip_address_redemption VARCHAR(40) DEFAULT NULL;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE generic_inference_jobs
|
||||
MODIFY status ENUM(
|
||||
'pending',
|
||||
'started',
|
||||
'complete_success',
|
||||
'complete_failure',
|
||||
'attempt_failed',
|
||||
'dead'
|
||||
) NOT NULL DEFAULT 'pending';
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- Add two states to allow us to distinguish user-cancelled jobs and system-cancelled jobs.
|
||||
ALTER TABLE generic_inference_jobs
|
||||
MODIFY status ENUM(
|
||||
'pending',
|
||||
'started',
|
||||
'complete_success',
|
||||
'complete_failure',
|
||||
'attempt_failed',
|
||||
'dead',
|
||||
'cancelled_by_user',
|
||||
'cancelled_by_system'
|
||||
) NOT NULL DEFAULT 'pending';
|
||||
@@ -123,11 +123,19 @@ CREATE TABLE generic_inference_jobs (
|
||||
-- ========== DEVELOPMENT AND DEBUGGING METADATA ==========
|
||||
|
||||
-- If true, the request gets a "debug" flag, which may do different
|
||||
-- things depending on the type of work.
|
||||
-- things depending on the type of work. This doesn't really have any
|
||||
-- specific meaning -- it can be used to flag for anything -- but should
|
||||
-- typically be used for one-off debugging and not a part of the normal
|
||||
-- product surface area. Contrast this with `maybe_routing_tag`, which
|
||||
-- forces the job onto a specific physical server: this might be a flag
|
||||
-- deployed to handle debugging across the entire fleet.
|
||||
is_debug_request BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- If set, the request gets processed by a special "tagged" worker
|
||||
-- that matches this tag. The ordinary workers will ignore this work.
|
||||
-- The tag is typically added via a special HTTP header when the work
|
||||
-- is enqueued. In practice, this means we can canary deploy or cordon
|
||||
-- off special workers to handle certain jobs.
|
||||
maybe_routing_tag VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- ========== JOB SYSTEM DETAILS ==========
|
||||
@@ -137,9 +145,11 @@ CREATE TABLE generic_inference_jobs (
|
||||
-- * Pending = job is ready to go
|
||||
-- * Started = job is running
|
||||
-- * Complete_Success = job is done (success)
|
||||
-- * Complete_Failure = job is done (failure)
|
||||
-- * Complete_Failure = job is done (permanent failure, eg. a Face Animator job without a face)
|
||||
-- * Attempt_Failed = job failed but may retry.
|
||||
-- * Dead = job failed permanently.
|
||||
-- * Dead = job failed permanently (ie. exhausted retries)
|
||||
-- * Cancelled_By_User = the user canceled their own job
|
||||
-- * Cancelled_By_System = the system terminated the job (ie. load shedding, user account deletion, etc.)
|
||||
--
|
||||
-- Pending -> Started -> Complete_Success
|
||||
-- |-> Complete_Failure
|
||||
@@ -150,17 +160,41 @@ CREATE TABLE generic_inference_jobs (
|
||||
'complete_success',
|
||||
'complete_failure',
|
||||
'attempt_failed',
|
||||
'dead') NOT NULL DEFAULT 'pending',
|
||||
'dead',
|
||||
'cancelled_by_user',
|
||||
'cancelled_by_system'
|
||||
) NOT NULL DEFAULT 'pending',
|
||||
|
||||
-- We can track this against a "max_attempt_count"
|
||||
attempt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- If there is a failure, tell the user why.
|
||||
-- This isn't localized or very friendly to present to the user.
|
||||
failure_reason VARCHAR(512) DEFAULT NULL,
|
||||
|
||||
-- Optional internal-only debugging information in the case of failure.
|
||||
internal_debugging_failure_reason VARCHAR(512) DEFAULT NULL,
|
||||
|
||||
-- An enum-like key to present the frontend with a failure class in an
|
||||
-- i18n-friendly way. The javascript/frontend can use these as indices
|
||||
-- into descriptive, broadly-localized error messages.
|
||||
--
|
||||
-- * 'face_not_detected' for images or videos that do not have a detectable
|
||||
-- face (SadTalker, Wav2Lip, etc.)
|
||||
--
|
||||
-- * 'keep_alive_elapsed' when users walk away from their inference job and
|
||||
-- the workload class expects for them to remain
|
||||
--
|
||||
-- * 'not_yet_implemented' error for developers during feature development
|
||||
-- and prototyping. Should ideally never happen in
|
||||
-- production
|
||||
--
|
||||
-- * 'retryable_worker_error' for generic worker errors (full filesystem,
|
||||
-- etc.) that can be retried. (We don't need to
|
||||
-- tell the user why the job failed.)
|
||||
--
|
||||
frontend_failure_category VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- On success, we populate how long the job execution took in milliseconds.
|
||||
-- We have this because 1) the job system timestamps are second-resolution,
|
||||
-- and 2) the structure of the jobs executor may change, potentially changing
|
||||
|
||||
@@ -19,12 +19,28 @@ CREATE TABLE media_files (
|
||||
|
||||
-- ========== FOREIGN KEY TO ORIGIN ==========
|
||||
|
||||
-- Where the file came from:
|
||||
-- Broad category for where the file came from:
|
||||
-- * 'inference' for inference output
|
||||
-- * 'processed' for processed file
|
||||
-- * 'upload' for direct user upload
|
||||
-- * 'processed' for processed file (eg. mp3 encoding, stem splitting, etc.)
|
||||
-- * 'upload' for direct user upload (from the filesystem)
|
||||
-- * 'device_api' for direct user uploads recorded using Browser/Device APIs.
|
||||
origin_category VARCHAR(16) NOT NULL,
|
||||
|
||||
-- TODO: Remove default value once records backfilled and queries updated.
|
||||
-- Product area where the media file originated.
|
||||
-- This is not the *model* that created the thing, this is the *product*.
|
||||
-- (The underlying models can change over time.)
|
||||
--
|
||||
-- This value indicates what product originally created the media file. (Not the ML model or
|
||||
-- user upload process.) This will let us scope media files to the product that generated them
|
||||
-- and filter them out of unrelated products if necessary (eg. a user probably doesn't want
|
||||
-- "Voice Designer" dataset samples in a video generation flow.)
|
||||
--
|
||||
-- Possible values:
|
||||
-- * 'face_animator' for uploads and outputs
|
||||
-- * 'unknown' for legacy records without an associated product (TODO: This should be temporary)
|
||||
origin_product_category VARCHAR(16) NOT NULL DEFAULT "unknown",
|
||||
|
||||
-- For inference that can be tied back to a model, the type of model.
|
||||
-- * 'sad_talker', which doesn't have a value for maybe_origin_model_token (!!!)
|
||||
-- * (more tome come)
|
||||
@@ -88,8 +104,24 @@ CREATE TABLE media_files (
|
||||
|
||||
-- The hash for the bucket directory that contains the original upload
|
||||
-- as well as any transcodings, downsamplings, etc.
|
||||
-- The bucket filename for the primary file (not including the directory path) is given by:
|
||||
-- `[{maybe_public_bucket_prefix}]{public_bucket_directory_hash}[{maybe_public_bucket_extension}]`
|
||||
public_bucket_directory_hash VARCHAR(32) NOT NULL,
|
||||
|
||||
-- An optional prefix on the bucket filename.
|
||||
-- If present, this will be prepended to the beginning of the bucket filename to access the file.
|
||||
-- The bucket filename for the primary file (not including the directory path) is given by:
|
||||
-- `[{maybe_public_bucket_prefix}]{public_bucket_directory_hash}[{maybe_public_bucket_extension}]`
|
||||
maybe_public_bucket_prefix VARCHAR(16) DEFAULT NULL,
|
||||
|
||||
-- An optional appended extension on the bucket filename.
|
||||
-- If present, this will be appended to the end of the bucket filename to access the file.
|
||||
-- To allow for flexibility, this extension typically will contain a leading period if
|
||||
-- the file needs it (eg ".mp4" rather than "mp4")!
|
||||
-- The bucket filename for the primary file (not including the directory path) is given by:
|
||||
-- `[{maybe_public_bucket_prefix}]{public_bucket_directory_hash}[{maybe_public_bucket_extension}]`
|
||||
maybe_public_bucket_extension VARCHAR(16) DEFAULT NULL,
|
||||
|
||||
-- NB: Removed, since this can be derived.
|
||||
-- The directory this media is uploaded to will be exclusive for this file.
|
||||
-- Only this given record will live in this bucket, but the directory may include
|
||||
|
||||
@@ -98,7 +98,7 @@ CREATE TABLE media_uploads (
|
||||
'public',
|
||||
'hidden',
|
||||
'private'
|
||||
) NOT NULL DEFAULT 'public',
|
||||
) NOT NULL DEFAULT 'public',
|
||||
|
||||
-- The synthetic id associated with this result.
|
||||
-- These ids are incremented on a per-user basis to help users
|
||||
|
||||
@@ -83,9 +83,10 @@ CREATE TABLE tts_models (
|
||||
-- ========== UNSTRUCTURED METADATA ==========
|
||||
|
||||
-- The full IETF BCP47 language tag (eg. en, en-US, es-419, ja-JP, pt, etc.)
|
||||
-- The wide varchar space is because these language tags can become cumbersome, eg "hy-Latn-IT-arevela"
|
||||
ietf_language_tag VARCHAR(64) NOT NULL DEFAULT 'en',
|
||||
|
||||
-- The IETF BCP47 language tag's primary language subtag (eg. "es-419" becomes "es")
|
||||
-- The IETF BCP47 language tag's primary language subtag (eg. "es-419" becomes "es" and "en-US" becomes "en")
|
||||
ietf_primary_language_subtag VARCHAR(12) NOT NULL DEFAULT 'en',
|
||||
|
||||
-- The "name" of the voice model, which might be complicated.
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- Per-user password resets.
|
||||
-- Password resets are valid until any of the following are true:
|
||||
--
|
||||
-- 1) database NOW() > password_rests.expires_at
|
||||
-- 2) password_rests.is_redeemed == TRUE
|
||||
-- 3) users.password_version > password_resets.current_password_version
|
||||
--
|
||||
-- When creating a new password reset, send the user an email with the `secret_key` in the URL.
|
||||
-- The user will have to supply their username and/or email address, plus the secret key to
|
||||
-- redeem the password reset.
|
||||
--
|
||||
-- Also when creating the password hash, be sure to set `expires_at` to be "NOW() + 3 hours", or
|
||||
-- some other small, reasonable time frame.
|
||||
--
|
||||
CREATE TABLE password_resets (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Password hash "primary key"
|
||||
-- (ie. for the future when we have a support dashboard)
|
||||
token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Foreign key to user
|
||||
user_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Secret "key" for the password reset
|
||||
-- This will be shared via URL or user text input.
|
||||
secret_key VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Copied from the user record at the time of password reset issuance.
|
||||
-- If the user's password version is greater than this value, then this
|
||||
-- reset is no longer valid.
|
||||
current_password_version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- Whether the password reset has been consumed.
|
||||
is_redeemed BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
-- For abuse tracking.
|
||||
-- Wide enough for IPv4/6
|
||||
ip_address_creation VARCHAR(40) NOT NULL,
|
||||
ip_address_redemption VARCHAR(40) DEFAULT NULL,
|
||||
|
||||
-- Incremented with every update.
|
||||
version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- ========== TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- When the password reset expires
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
|
||||
-- INDICES --
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (token),
|
||||
|
||||
UNIQUE KEY (user_token, secret_key),
|
||||
|
||||
KEY fk_user_token (user_token),
|
||||
KEY index_secret_key (secret_key),
|
||||
KEY index_expires_at (expires_at)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -0,0 +1,89 @@
|
||||
-- NB: This is a manually squashed view of all the CREATE and ALTER statements,
|
||||
-- with comments attached to the fields for centralized documentation.
|
||||
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- These are the individual files in each dataset.
|
||||
-- Files can be added and removed from a dataset at any time.
|
||||
CREATE TABLE zs_voice_dataset_samples (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Effective "primary key" for joins, API, etc. (PUBLIC)
|
||||
token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Idempotency token from client
|
||||
-- This is so the frontend client doesn't submit duplicate uploads.
|
||||
uuid_idempotency_token VARCHAR(36) NOT NULL,
|
||||
|
||||
-- ========== ZERO SHOT FOREIGN KEYS AND STATE==========
|
||||
|
||||
-- The zero shot dataset that was used to create this voice.
|
||||
-- The dataset will only be accessible to the original author of the voice.
|
||||
-- The original author can create multiple voices from the same dataset.
|
||||
dataset_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Foreign key to the uploaded media file.
|
||||
-- There was an option of handling uploads and file management separately, but by electing to store the dataset
|
||||
-- samples in the "media_files" table, they can be reused by our other code, queries, machinery, etc.
|
||||
-- Note that this requires that the "media_files" record must be an audio (not video) file (which our code will
|
||||
-- enforce), and that it is given a category of "voice_zero_shot" so it isn't co-mingled with other uploads/outputs
|
||||
media_file_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Some data sets require raw transcripts for the audio.
|
||||
-- If required, they'll live here.
|
||||
maybe_raw_transcription TEXT DEFAULT NULL,
|
||||
|
||||
-- A way to order the data set samples
|
||||
-- We don't necessarily need to reimplement file reordering.
|
||||
-- order_id INT DEFAULT NULL,
|
||||
|
||||
-- ========== CREATOR DETAILS AND PREFERENCES ==========
|
||||
|
||||
-- Foreign key to user
|
||||
maybe_creator_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- Based on a cookie sent by the frontend.
|
||||
-- We'll save this even if the user is logged in.
|
||||
maybe_creator_anonymous_visitor_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- For abuse tracking.
|
||||
-- Wide enough for IPv4/6
|
||||
creator_ip_address VARCHAR(40) NOT NULL,
|
||||
|
||||
-- ========== MODERATION DETAILS ==========
|
||||
|
||||
-- The last moderator that made changes last.
|
||||
-- Fine details of moderator actions are kept in a separate `audit_logs` table.
|
||||
maybe_mod_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- ========== TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- If this is removed by a mod or the creator.
|
||||
user_deleted_at TIMESTAMP NULL,
|
||||
mod_deleted_at TIMESTAMP NULL,
|
||||
|
||||
-- ========== VECTOR CLOCK ==========
|
||||
|
||||
-- Incremented with every update.
|
||||
version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- INDICES --
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (token),
|
||||
UNIQUE KEY (uuid_idempotency_token),
|
||||
|
||||
KEY fk_dataset_token (dataset_token),
|
||||
KEY fk_media_file_token (media_file_token),
|
||||
KEY fk_maybe_creator_user_token (maybe_creator_user_token),
|
||||
KEY fk_maybe_mod_user_token (maybe_mod_user_token),
|
||||
KEY index_creator_ip_address (creator_ip_address),
|
||||
KEY index_user_deleted_at (user_deleted_at),
|
||||
KEY index_mod_deleted_at (mod_deleted_at)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -0,0 +1,109 @@
|
||||
-- NB: This is a manually squashed view of all the CREATE and ALTER statements,
|
||||
-- with comments attached to the fields for centralized documentation.
|
||||
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- This table describes a zero shot voice in the process of being created (or later reused / edited / enhanced)
|
||||
-- These are not usable voices until their vector embeddings have been created, and this table does not serve
|
||||
-- the production voice list.
|
||||
--
|
||||
-- Multiple voices can be created from a single "dataset" as the data set is mutable over time. Users can add or
|
||||
-- remove new samples to the data set at any time.
|
||||
CREATE TABLE zs_voice_datasets (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Effective "primary key" for joins, API, etc. (PUBLIC)
|
||||
token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- NB: No idempotency token.
|
||||
-- Store it in another table to prevent duplicate record creation from rapid user inputs.
|
||||
|
||||
-- ========== VOICE DETAILS ==========
|
||||
|
||||
-- The "title" of the voice. This can be the name and additional information as supplied by the user.
|
||||
-- When the voice embeddings are created, this will be copied to the produced "voice" (embeddings) record.
|
||||
title VARCHAR(255) NOT NULL,
|
||||
|
||||
-- The full IETF BCP47 language tag (eg. en, en-US, es-419, ja-JP, pt, etc.)
|
||||
-- The wide varchar space is because these language tags can become cumbersome, eg "hy-Latn-IT-arevela"
|
||||
ietf_language_tag VARCHAR(64) NOT NULL DEFAULT 'en',
|
||||
|
||||
-- The IETF BCP47 language tag's primary language subtag (eg. "es-419" becomes "es" and "en-US" becomes "en")
|
||||
ietf_primary_language_subtag VARCHAR(12) NOT NULL DEFAULT 'en',
|
||||
|
||||
-- TODO: misc speaker metadata
|
||||
-- We'll want rich metadata for search
|
||||
-- When the voice embeddings are created, this will be copied to the produced "voice" (embeddings) record.
|
||||
-- We don't want a hard link between the dataset and the produced voice as the voice is static once
|
||||
-- created (with the exception of regenerating embeddings), but the dataset my drift as the user experiments,
|
||||
-- adds/removes samples, etc.
|
||||
-- series_name VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_name VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_gender VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_age VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_voice_style VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
-- ========== CREATOR DETAILS AND PREFERENCES ==========
|
||||
|
||||
-- Foreign key to user
|
||||
maybe_creator_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- Based on a cookie sent by the frontend.
|
||||
-- We'll save this even if the user is logged in.
|
||||
maybe_creator_anonymous_visitor_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- For abuse tracking.
|
||||
-- Wide enough for IPv4/6
|
||||
creator_ip_address VARCHAR(40) NOT NULL,
|
||||
|
||||
-- The creator can set a desired visibility for their data.
|
||||
-- This does not always apply to every upload type.
|
||||
-- Additionally, some upload types may require moderator approval prior
|
||||
-- to being publicly listed, and this field has no bearing on that.
|
||||
-- NB: DO NOT CHANGE THE ORDER OF THE ENUM VALUES DURING SCHEMA MIGRATIONS.
|
||||
creator_set_visibility ENUM(
|
||||
'public',
|
||||
'hidden',
|
||||
'private'
|
||||
) NOT NULL DEFAULT 'public',
|
||||
|
||||
-- The synthetic id associated with this result.
|
||||
-- These ids are incremented on a per-user basis to help users
|
||||
-- sequence their own work. They serve no other purpose.
|
||||
maybe_creator_synthetic_id BIGINT(20) DEFAULT NULL,
|
||||
|
||||
-- ========== MODERATION DETAILS ==========
|
||||
|
||||
-- The last moderator that made changes last.
|
||||
-- Fine details of moderator actions are kept in a separate `audit_logs` table.
|
||||
maybe_mod_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- ========== TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- If this is removed by a mod or the creator.
|
||||
user_deleted_at TIMESTAMP NULL,
|
||||
mod_deleted_at TIMESTAMP NULL,
|
||||
|
||||
-- ========== VECTOR CLOCK ==========
|
||||
|
||||
-- Incremented with every update.
|
||||
version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- INDICES --
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (token),
|
||||
|
||||
KEY index_ietf_primary_language_subtag (ietf_primary_language_subtag),
|
||||
KEY fk_maybe_creator_user_token (maybe_creator_user_token),
|
||||
KEY fk_maybe_mod_user_token (maybe_mod_user_token),
|
||||
KEY index_creator_ip_address (creator_ip_address),
|
||||
KEY index_user_deleted_at (user_deleted_at),
|
||||
KEY index_mod_deleted_at (mod_deleted_at)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -0,0 +1,157 @@
|
||||
-- NB: This is a manually squashed view of all the CREATE and ALTER statements,
|
||||
-- with comments attached to the fields for centralized documentation.
|
||||
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- This table contains both zero shot TTS voices (speaker embeddings) and zero shot V2V voices (speaker embeddings).
|
||||
-- These are the zero shot voices that are usable by our users.
|
||||
CREATE TABLE zs_voices (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Effective "primary key" for joins, API, etc. (PUBLIC)
|
||||
token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- NB: No idempotency token.
|
||||
-- Store it in another table to prevent duplicate record creation from rapid user inputs.
|
||||
|
||||
-- ========== ZERO SHOT FOREIGN KEYS AND STATE==========
|
||||
|
||||
-- The zero shot dataset that was used to create this voice.
|
||||
-- The dataset will only be accessible to the original author of the voice.
|
||||
-- The original author can create multiple voices from the same dataset.
|
||||
dataset_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- When datasets are edited, we bump their version.
|
||||
-- This tells us which version of the dataset created this voice (not that we should preserve
|
||||
-- record of which samples are in which version.)
|
||||
-- We can surface this to the users as well.
|
||||
dataset_version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- ========== MODEL DETAILS ==========
|
||||
|
||||
-- The class or category of model/algorithm this voice is intended for.
|
||||
-- * 'tts' for zero shot TTS
|
||||
-- * 'vc' for zero shot voice conversion
|
||||
-- * 'pitch' for pitch correction models (hypothetical)
|
||||
-- * (Since this isn't an enum, the list of values can be expanded.)
|
||||
model_category VARCHAR(16) NOT NULL,
|
||||
|
||||
-- The model used for the voice:
|
||||
-- * 'vall-e-x' for Vall-E-X
|
||||
-- * 'ns2vc' for NaturalSpeech2 voice conversion (hypothetical)
|
||||
-- * (Since this isn't an enum, the list of values can be expanded.)
|
||||
model_type VARCHAR(16) NOT NULL,
|
||||
|
||||
-- The version of the model.
|
||||
-- If we deploy new model weights for the model, we may need to regenerate
|
||||
-- all of the corresponding speaker embeddings. This lets us know which voices
|
||||
-- are the old version.
|
||||
-- In some cases, we may need to preserve, hide, or roll back to older versions.
|
||||
model_version INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- What kind of features these are: spectrogram, encodec, etc.
|
||||
-- Technically we shouldn't need this, as the 2-tuple (model_type, model_version) implicitly knows
|
||||
-- what its features should be, but this is probably useful for our own analysis and may be easier
|
||||
-- to deal with.
|
||||
--
|
||||
-- Example values:
|
||||
-- * 'spectrogram'
|
||||
-- * 'encodec'
|
||||
-- * (Since this isn't an enum, the list of values can be expanded.)
|
||||
encoding_type VARCHAR(16) NOT NULL,
|
||||
|
||||
-- ========== VOICE DETAILS ==========
|
||||
|
||||
-- The "title" of the voice. This can be the name and additional information as supplied by the user.
|
||||
title VARCHAR(255) NOT NULL,
|
||||
|
||||
-- The full IETF BCP47 language tag (eg. en, en-US, es-419, ja-JP, pt, etc.)
|
||||
-- The wide varchar space is because these language tags can become cumbersome, eg "hy-Latn-IT-arevela"
|
||||
ietf_language_tag VARCHAR(64) NOT NULL DEFAULT 'en',
|
||||
|
||||
-- The IETF BCP47 language tag's primary language subtag (eg. "es-419" becomes "es" and "en-US" becomes "en")
|
||||
ietf_primary_language_subtag VARCHAR(12) NOT NULL DEFAULT 'en',
|
||||
|
||||
-- TODO: misc speaker metadata
|
||||
-- We'll want rich metadata for search
|
||||
-- series_name VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_name VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_gender VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_age VARCHAR(255) DEFAULT NULL,
|
||||
-- speaker_voice_style VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
-- ========== STORAGE DETAILS ==========
|
||||
|
||||
-- The hash for the bucket directory that contains the original upload
|
||||
-- eg. /speaker_vectors/{bucket_hash}/{voice_model_version}.bin
|
||||
bucket_hash VARCHAR(32) NOT NULL,
|
||||
|
||||
-- ========== CREATOR DETAILS AND PREFERENCES ==========
|
||||
|
||||
-- Foreign key to user
|
||||
maybe_creator_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- Based on a cookie sent by the frontend.
|
||||
-- We'll save this even if the user is logged in.
|
||||
maybe_creator_anonymous_visitor_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- For abuse tracking.
|
||||
-- Wide enough for IPv4/6
|
||||
creator_ip_address VARCHAR(40) NOT NULL,
|
||||
|
||||
-- The creator can set a desired visibility for their data.
|
||||
-- This does not always apply to every upload type.
|
||||
-- Additionally, some upload types may require moderator approval prior
|
||||
-- to being publicly listed, and this field has no bearing on that.
|
||||
-- NB: DO NOT CHANGE THE ORDER OF THE ENUM VALUES DURING SCHEMA MIGRATIONS.
|
||||
creator_set_visibility ENUM(
|
||||
'public',
|
||||
'hidden',
|
||||
'private'
|
||||
) NOT NULL DEFAULT 'public',
|
||||
|
||||
-- The synthetic id associated with this result.
|
||||
-- These ids are incremented on a per-user basis to help users
|
||||
-- sequence their own work. They serve no other purpose.
|
||||
maybe_creator_synthetic_id BIGINT(20) DEFAULT NULL,
|
||||
|
||||
-- ========== MODERATION DETAILS ==========
|
||||
|
||||
-- The last moderator that made changes last.
|
||||
-- Fine details of moderator actions are kept in a separate `audit_logs` table.
|
||||
maybe_mod_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- ========== TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- If this is removed by a mod or the creator.
|
||||
user_deleted_at TIMESTAMP NULL,
|
||||
mod_deleted_at TIMESTAMP NULL,
|
||||
|
||||
-- ========== VECTOR CLOCK ==========
|
||||
|
||||
-- Incremented with every update.
|
||||
version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- INDICES --
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (token),
|
||||
|
||||
-- TODO: These are not correct. Fix these before landing / running migrations.
|
||||
KEY index_model_category (model_category),
|
||||
KEY index_model_type (model_type),
|
||||
KEY index_model_type_and_version (model_type, model_version),
|
||||
KEY index_encoding_type (encoding_type),
|
||||
KEY index_ietf_primary_language_subtag (ietf_primary_language_subtag),
|
||||
KEY fk_maybe_creator_user_token (maybe_creator_user_token),
|
||||
KEY fk_maybe_mod_user_token (maybe_mod_user_token),
|
||||
KEY index_creator_ip_address (creator_ip_address),
|
||||
KEY index_user_deleted_at (user_deleted_at),
|
||||
KEY index_mod_deleted_at (mod_deleted_at)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+6
-3
@@ -18,7 +18,7 @@ build_shared_mysql_database_library() {
|
||||
# NB: For now, this is our monolithic DB that serves all of our microservices (gross)
|
||||
# It's a single package so that queries can be shared (again, gross)
|
||||
pushd crates/lib/mysql_queries
|
||||
SQLX_OFFLINE=true cargo sqlx prepare --merged
|
||||
SQLX_OFFLINE=true cargo sqlx prepare
|
||||
popd
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ build_shared_sqlite_database_library() {
|
||||
echo 'mysql prepare'
|
||||
build_shared_mysql_database_library
|
||||
|
||||
echo 'sqlite prepare'
|
||||
build_shared_sqlite_database_library
|
||||
#echo 'sqlite prepare'
|
||||
#build_shared_sqlite_database_library
|
||||
|
||||
echo 'done'
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crockford::crockford_entropy_lower;
|
||||
|
||||
use crate::public::public_path::PublicPath;
|
||||
use crate::util::hashed_directory_path_long_string::hashed_directory_path_long_string;
|
||||
use crockford::crockford_entropy_lower;
|
||||
|
||||
// TODO: Generate these from a macro.
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crockford::crockford_entropy_lower;
|
||||
|
||||
use crate::public::media_files::directory::MediaFileBucketDirectory;
|
||||
use crate::public::public_path::PublicPath;
|
||||
use crockford::crockford_entropy_lower;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const ORIGINAL_FILE_BASENAME : &str = "file.bin";
|
||||
|
||||
@@ -12,25 +14,43 @@ const ORIGINAL_FILE_BASENAME : &str = "file.bin";
|
||||
#[derive(Clone)]
|
||||
pub struct MediaFileBucketPath {
|
||||
directory: MediaFileBucketDirectory,
|
||||
|
||||
filename: String,
|
||||
full_object_path: String,
|
||||
|
||||
optional_prefix: Option<String>,
|
||||
/// NB: Extension contains the leading period, eg. ".mp4"
|
||||
optional_extension: Option<String>,
|
||||
}
|
||||
|
||||
impl PublicPath for MediaFileBucketPath {}
|
||||
|
||||
impl MediaFileBucketPath {
|
||||
|
||||
pub fn generate_new() -> Self {
|
||||
pub fn generate_new(optional_prefix: Option<&str>, optional_extension: Option<&str>) -> Self {
|
||||
let entropy = crockford_entropy_lower(32);
|
||||
Self::from_object_hash(&entropy)
|
||||
Self::from_object_hash(&entropy, optional_prefix, optional_extension)
|
||||
}
|
||||
|
||||
pub fn from_object_hash(hash: &str) -> Self {
|
||||
pub fn from_object_hash(hash: &str, optional_prefix: Option<&str>, optional_extension: Option<&str>) -> Self {
|
||||
// TODO: Path construction could be cleaner.
|
||||
let directory = MediaFileBucketDirectory::from_object_hash(hash);
|
||||
let full_object_path = format!("{}/{}", directory.get_directory_path_str(), ORIGINAL_FILE_BASENAME);
|
||||
|
||||
let filename = match (optional_prefix, optional_extension) {
|
||||
(None, None) => ORIGINAL_FILE_BASENAME.to_string(),
|
||||
(None, Some(ext)) => format!("{}{}", hash, ext),
|
||||
(Some(pre), None) => format!("{}{}", pre, hash),
|
||||
(Some(pre), Some(ext)) => format!("{}{}{}", pre, hash, ext),
|
||||
};
|
||||
|
||||
let full_object_path = format!("{}/{}", directory.get_directory_path_str(), filename);
|
||||
|
||||
Self {
|
||||
directory,
|
||||
filename,
|
||||
full_object_path,
|
||||
optional_prefix: optional_prefix.map(|p| p.to_string()),
|
||||
optional_extension: optional_extension.map(|e| e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,52 +70,76 @@ impl MediaFileBucketPath {
|
||||
&self.directory.get_object_hash()
|
||||
}
|
||||
|
||||
pub fn get_basename() -> &'static str {
|
||||
ORIGINAL_FILE_BASENAME
|
||||
pub fn get_basename(&self) -> &str {
|
||||
&self.filename
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::public::media_files::original_file::MediaFileBucketPath;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
pub fn generate_new_entropy() {
|
||||
let file = MediaFileBucketPath::generate_new();
|
||||
assert_eq!(file.get_object_hash().len(), 32);
|
||||
assert_eq!(file.get_directory().get_object_hash().len(), 32);
|
||||
use crate::public::media_files::original_file::MediaFileBucketPath;
|
||||
|
||||
mod with_prefix_and_extension {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn generate_new_entropy() {
|
||||
let file = MediaFileBucketPath::generate_new(Some("pre_"), Some(".mp4"));
|
||||
assert_eq!(file.get_object_hash().len(), 32);
|
||||
assert_eq!(file.get_object_hash().len(), 32);
|
||||
assert_eq!(file.get_directory().get_object_hash().len(), 32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn get_full_object_path_str() {
|
||||
let file = MediaFileBucketPath::from_object_hash("abcdefghijk", Some("pre_"), Some(".mp4"));
|
||||
assert_eq!(file.get_full_object_path_str(), "/media/a/b/c/d/e/abcdefghijk/pre_abcdefghijk.mp4");
|
||||
assert_eq!(file.get_basename(), "pre_abcdefghijk.mp4");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn get_full_object_path_str() {
|
||||
let file = MediaFileBucketPath::from_object_hash("abcdefghijk");
|
||||
assert_eq!(file.get_full_object_path_str(), "/media/a/b/c/d/e/abcdefghijk/file.bin");
|
||||
}
|
||||
mod without_prefix_and_extension {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn to_full_object_pathbuf() {
|
||||
let file = MediaFileBucketPath::from_object_hash("abcdefghijk");
|
||||
assert_eq!(file.to_full_object_pathbuf(), PathBuf::from("/media/a/b/c/d/e/abcdefghijk/file.bin"));
|
||||
}
|
||||
#[test]
|
||||
pub fn generate_new_entropy() {
|
||||
let file = MediaFileBucketPath::generate_new(None, None);
|
||||
assert_eq!(file.get_object_hash().len(), 32);
|
||||
assert_eq!(file.get_directory().get_object_hash().len(), 32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn get_full_object_path_str_short_name() {
|
||||
let file = MediaFileBucketPath::from_object_hash("foo");
|
||||
assert_eq!(file.get_full_object_path_str(), "/media/f/o/foo/file.bin");
|
||||
}
|
||||
#[test]
|
||||
pub fn get_full_object_path_str() {
|
||||
let file = MediaFileBucketPath::from_object_hash("abcdefghijk", None, None);
|
||||
assert_eq!(file.get_full_object_path_str(), "/media/a/b/c/d/e/abcdefghijk/file.bin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn get_full_object_path_str_starts_with_directory() {
|
||||
let file = MediaFileBucketPath::from_object_hash("abcdefghijk");
|
||||
let full_path = file.get_full_object_path_str();
|
||||
assert!(full_path.starts_with(file.get_directory().get_directory_path_str()))
|
||||
}
|
||||
#[test]
|
||||
pub fn to_full_object_pathbuf() {
|
||||
let file = MediaFileBucketPath::from_object_hash("abcdefghijk", None, None);
|
||||
assert_eq!(file.to_full_object_pathbuf(), PathBuf::from("/media/a/b/c/d/e/abcdefghijk/file.bin"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn get_object_hash() {
|
||||
let hash = "abcdefghijk";
|
||||
let file = MediaFileBucketPath::from_object_hash(hash);
|
||||
assert_eq!(file.get_object_hash(), hash);
|
||||
#[test]
|
||||
pub fn get_full_object_path_str_short_name() {
|
||||
let file = MediaFileBucketPath::from_object_hash("foo", None, None);
|
||||
assert_eq!(file.get_full_object_path_str(), "/media/f/o/foo/file.bin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn get_full_object_path_str_starts_with_directory() {
|
||||
let file = MediaFileBucketPath::from_object_hash("abcdefghijk", None, None);
|
||||
let full_path = file.get_full_object_path_str();
|
||||
assert!(full_path.starts_with(file.get_directory().get_directory_path_str()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn get_object_hash() {
|
||||
let hash = "abcdefghijk";
|
||||
let file = MediaFileBucketPath::from_object_hash(hash, None, None);
|
||||
assert_eq!(file.get_object_hash(), hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crockford::crockford_entropy_lower;
|
||||
|
||||
use crate::public::public_path::PublicPath;
|
||||
use crate::util::hashed_directory_path_long_string::hashed_directory_path_long_string;
|
||||
use crockford::crockford_entropy_lower;
|
||||
|
||||
// TODO: Generate these from a macro.
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crockford::crockford_entropy_lower;
|
||||
|
||||
use crate::public::media_uploads::directory::MediaUploadDirectory;
|
||||
use crate::public::public_path::PublicPath;
|
||||
use crockford::crockford_entropy_lower;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const ORIGINAL_FILE_BASENAME : &str = "original_upload.bin";
|
||||
|
||||
@@ -57,9 +59,10 @@ impl MediaUploadOriginalFilePath {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::public::media_uploads::original_file::MediaUploadOriginalFilePath;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::public::media_uploads::original_file::MediaUploadOriginalFilePath;
|
||||
|
||||
#[test]
|
||||
pub fn generate_new_entropy() {
|
||||
let file = MediaUploadOriginalFilePath::generate_new();
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crockford::crockford_entropy_lower;
|
||||
|
||||
use crate::public::public_path::PublicPath;
|
||||
use crate::public::voice_conversion_results::directory::VoiceConversionResultDirectory;
|
||||
use crockford::crockford_entropy_lower;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const ORIGINAL_FILE_PREFIX : &str = "fakeyou_";
|
||||
const ORIGINAL_FILE_SUFFIX : &str = ".wav";
|
||||
@@ -61,9 +63,10 @@ impl VoiceConversionResultOriginalFilePath {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::public::voice_conversion_results::original_file::VoiceConversionResultOriginalFilePath;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::public::voice_conversion_results::original_file::VoiceConversionResultOriginalFilePath;
|
||||
|
||||
#[test]
|
||||
pub fn generate_new_entropy() {
|
||||
let file = VoiceConversionResultOriginalFilePath::generate_new();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use errors::bail;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[deprecated(note = "this is a messy function and should be avoided")]
|
||||
pub (crate) fn hash_to_bucket_path(file_hash: &str,
|
||||
@@ -28,9 +29,10 @@ pub (crate) fn hash_to_bucket_path(file_hash: &str,
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::util::hash_to_bucket_path::hash_to_bucket_path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::util::hash_to_bucket_path::hash_to_bucket_path;
|
||||
|
||||
#[test]
|
||||
fn test_bucket_without_root() {
|
||||
assert_eq!(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::util::hash_to_bucket_path::hash_to_bucket_path;
|
||||
use errors::AnyhowResult;
|
||||
use errors::anyhow;
|
||||
use errors::AnyhowResult;
|
||||
|
||||
use crate::util::hash_to_bucket_path::hash_to_bucket_path;
|
||||
|
||||
#[deprecated(note = "this is a messy function and should be avoided")]
|
||||
pub fn hash_to_bucket_path_string(file_hash: &str,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
|
||||
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `frontend_failure_category`.
|
||||
///
|
||||
/// When jobs fail (permanently or transiently), we need to inform the frontend of the reason,
|
||||
/// because perhaps there's something the user can do to change their input.
|
||||
///
|
||||
/// The previous "VARCHAR(32) failure_reason" column was a text-based message that could not be
|
||||
/// localized or made user friendly. This `frontend_failure_category` exists to provide well-defined
|
||||
/// failure categories to the frontend that can easily be localized and indicated consistently in
|
||||
/// the UI.
|
||||
///
|
||||
/// Another benefit is that we'll surface all of the various types of failure and perhaps eventually
|
||||
/// come to handle some in a cross-cutting way.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
pub enum FrontendFailureCategory {
|
||||
/// When a face is not detected in the image used for animation.
|
||||
/// For SadTalker (and possibly Wav2Lip)
|
||||
#[serde(rename = "face_not_detected")]
|
||||
FaceNotDetected,
|
||||
|
||||
/// The user stepped away from their device and expected the workload to finish.
|
||||
/// Some workloads require that the user keep their browser open.
|
||||
#[serde(rename = "keep_alive_elapsed")]
|
||||
KeepAliveElapsed,
|
||||
|
||||
/// This is mostly for developers -- a feature isn't complete somewhere in the code.
|
||||
/// Big oops if errors of this class make it to production.
|
||||
#[serde(rename = "not_yet_implemented")]
|
||||
NotYetImplemented,
|
||||
|
||||
/// Tell the user that some kind of transient error happened. They don't need to know
|
||||
/// exactly what happened. We'll retry their workload in any case.
|
||||
#[serde(rename = "retryable_worker_error")]
|
||||
RetryableWorkerError,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
impl_enum_display_and_debug_using_to_str!(FrontendFailureCategory);
|
||||
impl_mysql_enum_coders!(FrontendFailureCategory);
|
||||
|
||||
/// NB: Legacy API for older code.
|
||||
impl FrontendFailureCategory {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::FaceNotDetected => "face_not_detected",
|
||||
Self::KeepAliveElapsed => "keep_alive_elapsed",
|
||||
Self::NotYetImplemented => "not_yet_implemented",
|
||||
Self::RetryableWorkerError => "retryable_worker_error",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(value: &str) -> Result<Self, String> {
|
||||
match value {
|
||||
"face_not_detected" => Ok(Self::FaceNotDetected),
|
||||
"keep_alive_elapsed" => Ok(Self::KeepAliveElapsed),
|
||||
"not_yet_implemented" => Ok(Self::NotYetImplemented),
|
||||
"retryable_worker_error" => Ok(Self::RetryableWorkerError),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants() -> BTreeSet<Self> {
|
||||
// NB: BTreeSet is sorted
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::FaceNotDetected,
|
||||
Self::KeepAliveElapsed,
|
||||
Self::NotYetImplemented,
|
||||
Self::RetryableWorkerError,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(FrontendFailureCategory::FaceNotDetected, "face_not_detected");
|
||||
assert_serialization(FrontendFailureCategory::KeepAliveElapsed, "keep_alive_elapsed");
|
||||
assert_serialization(FrontendFailureCategory::NotYetImplemented, "not_yet_implemented");
|
||||
assert_serialization(FrontendFailureCategory::RetryableWorkerError, "retryable_worker_error");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(FrontendFailureCategory::FaceNotDetected.to_str(), "face_not_detected");
|
||||
assert_eq!(FrontendFailureCategory::KeepAliveElapsed.to_str(), "keep_alive_elapsed");
|
||||
assert_eq!(FrontendFailureCategory::NotYetImplemented.to_str(), "not_yet_implemented");
|
||||
assert_eq!(FrontendFailureCategory::RetryableWorkerError.to_str(), "retryable_worker_error");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(FrontendFailureCategory::from_str("face_not_detected").unwrap(), FrontendFailureCategory::FaceNotDetected);
|
||||
assert_eq!(FrontendFailureCategory::from_str("keep_alive_elapsed").unwrap(), FrontendFailureCategory::KeepAliveElapsed);
|
||||
assert_eq!(FrontendFailureCategory::from_str("not_yet_implemented").unwrap(), FrontendFailureCategory::NotYetImplemented);
|
||||
assert_eq!(FrontendFailureCategory::from_str("retryable_worker_error").unwrap(), FrontendFailureCategory::RetryableWorkerError);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = FrontendFailureCategory::all_variants();
|
||||
assert_eq!(variants.len(), 4);
|
||||
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::FaceNotDetected));
|
||||
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::KeepAliveElapsed));
|
||||
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::NotYetImplemented));
|
||||
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::RetryableWorkerError));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(FrontendFailureCategory::all_variants().len(), FrontendFailureCategory::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in FrontendFailureCategory::all_variants() {
|
||||
assert_eq!(variant, FrontendFailureCategory::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, FrontendFailureCategory::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, FrontendFailureCategory::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
|
||||
|
||||
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `inference_category`.
|
||||
///
|
||||
/// Our "generic inference" pipeline supports a wide variety of ML models and other media.
|
||||
|
||||
-1
@@ -5,7 +5,6 @@ use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
|
||||
|
||||
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `maybe_input_source_token`.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
|
||||
@@ -5,7 +5,6 @@ use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
|
||||
|
||||
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `maybe_model_type`.
|
||||
///
|
||||
/// Our "generic inference" pipeline supports a wide variety of ML models and other media.
|
||||
@@ -20,20 +19,18 @@ use strum::EnumIter;
|
||||
pub enum InferenceModelType {
|
||||
#[serde(rename = "rvc_v2")]
|
||||
RvcV2,
|
||||
|
||||
// NB: sad_talker does use user-supplied models, so there is no "model token"
|
||||
#[serde(rename = "sad_talker")]
|
||||
SadTalker,
|
||||
|
||||
#[serde(rename = "so_vits_svc")]
|
||||
SoVitsSvc,
|
||||
|
||||
// TODO: Does this need to be "legacy_tacotron2" ?
|
||||
#[serde(rename = "tacotron2")]
|
||||
Tacotron2,
|
||||
|
||||
#[serde(rename = "vits")]
|
||||
Vits,
|
||||
#[serde(rename = "vall_e_x")]
|
||||
VallEX,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
@@ -49,6 +46,7 @@ impl InferenceModelType {
|
||||
Self::SoVitsSvc => "so_vits_svc",
|
||||
Self::Tacotron2 => "tacotron2",
|
||||
Self::Vits => "vits",
|
||||
Self::VallEX => "vall_e_x"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +57,7 @@ impl InferenceModelType {
|
||||
"so_vits_svc" => Ok(Self::SoVitsSvc),
|
||||
"tacotron2" => Ok(Self::Tacotron2),
|
||||
"vits" => Ok(Self::Vits),
|
||||
"vall_e_x" => Ok(Self::VallEX),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
@@ -72,6 +71,7 @@ impl InferenceModelType {
|
||||
InferenceModelType::SoVitsSvc,
|
||||
InferenceModelType::Tacotron2,
|
||||
InferenceModelType::Vits,
|
||||
InferenceModelType::VallEX
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,7 @@ mod tests {
|
||||
assert_serialization(InferenceModelType::SoVitsSvc, "so_vits_svc");
|
||||
assert_serialization(InferenceModelType::Tacotron2, "tacotron2");
|
||||
assert_serialization(InferenceModelType::Vits, "vits");
|
||||
assert_serialization(InferenceModelType::VallEX, "vall_e_x");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -97,6 +98,7 @@ mod tests {
|
||||
assert_eq!(InferenceModelType::SoVitsSvc.to_str(), "so_vits_svc");
|
||||
assert_eq!(InferenceModelType::Tacotron2.to_str(), "tacotron2");
|
||||
assert_eq!(InferenceModelType::Vits.to_str(), "vits");
|
||||
assert_eq!(InferenceModelType::VallEX.to_str(), "vall_e_x");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -106,18 +108,20 @@ mod tests {
|
||||
assert_eq!(InferenceModelType::from_str("so_vits_svc").unwrap(), InferenceModelType::SoVitsSvc);
|
||||
assert_eq!(InferenceModelType::from_str("tacotron2").unwrap(), InferenceModelType::Tacotron2);
|
||||
assert_eq!(InferenceModelType::from_str("vits").unwrap(), InferenceModelType::Vits);
|
||||
assert_eq!(InferenceModelType::from_str("vall_e_x").unwrap(), InferenceModelType::VallEX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
// Static check
|
||||
let mut variants = InferenceModelType::all_variants();
|
||||
assert_eq!(variants.len(), 5);
|
||||
assert_eq!(variants.len(), 6);
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::RvcV2));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::SadTalker));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::SoVitsSvc));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::Tacotron2));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::Vits));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceModelType::VallEX));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
|
||||
// Generated check
|
||||
|
||||
@@ -5,7 +5,6 @@ use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
|
||||
|
||||
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `on_success_result_entity_type`.
|
||||
///
|
||||
/// Our "generic inference" pipeline supports a wide variety of output types.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod frontend_failure_category;
|
||||
pub mod inference_category;
|
||||
pub mod inference_input_source_token_type;
|
||||
pub mod inference_model_type;
|
||||
|
||||
@@ -5,7 +5,6 @@ use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
|
||||
|
||||
/// Used in the `generic_synthetic_ids` table in `VARCHAR(32)` field `id_category`.
|
||||
///
|
||||
/// This lets us create synthetic increment IDs on a per-user, per-category basis.
|
||||
@@ -21,6 +20,23 @@ pub enum IdCategory {
|
||||
/// Results from lipsync animations (which may live in the media_files table)
|
||||
#[serde(rename = "lipsync_animation")]
|
||||
LipsyncAnimation,
|
||||
|
||||
/// Results from voice conversion (which may live in the media_files table)
|
||||
/// Applies for RVC and SVC
|
||||
#[serde(rename = "voice_conversion")]
|
||||
VoiceConversion,
|
||||
|
||||
/// Results from the zero shot tts
|
||||
#[serde(rename = "zs_audio_tts")]
|
||||
ZeroShotTTS,
|
||||
|
||||
/// Zs dataset which lives in the zs_voice_datasets table
|
||||
#[serde(rename = "zs_dataset")]
|
||||
ZsDataset,
|
||||
|
||||
/// Zs voice which lives in the zs_voices table
|
||||
#[serde(rename = "zs_voice")]
|
||||
ZsVoice,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
@@ -33,6 +49,10 @@ impl IdCategory {
|
||||
match self {
|
||||
Self::MediaFile => "media_file",
|
||||
Self::LipsyncAnimation => "lipsync_animation",
|
||||
Self::VoiceConversion => "voice_conversion",
|
||||
Self::ZsDataset => "zs_dataset",
|
||||
Self::ZsVoice => "zs_voice",
|
||||
Self::ZeroShotTTS => "zs_audio_tts"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +60,10 @@ impl IdCategory {
|
||||
match value {
|
||||
"media_file" => Ok(Self::MediaFile),
|
||||
"lipsync_animation" => Ok(Self::LipsyncAnimation),
|
||||
"voice_conversion" => Ok(Self::VoiceConversion),
|
||||
"zs_dataset" => Ok(Self::ZsDataset),
|
||||
"zs_voice" => Ok(Self::ZsVoice),
|
||||
"zs_audio_tts" => Ok(Self::ZeroShotTTS),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
@@ -50,6 +74,10 @@ impl IdCategory {
|
||||
BTreeSet::from([
|
||||
Self::MediaFile,
|
||||
Self::LipsyncAnimation,
|
||||
Self::VoiceConversion,
|
||||
Self::ZsDataset,
|
||||
Self::ZsVoice,
|
||||
Self::ZeroShotTTS
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -59,35 +87,73 @@ mod tests {
|
||||
use crate::by_table::generic_synthetic_ids::id_category::IdCategory;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(IdCategory::MediaFile, "media_file");
|
||||
assert_serialization(IdCategory::LipsyncAnimation, "lipsync_animation");
|
||||
assert_serialization(IdCategory::VoiceConversion, "voice_conversion");
|
||||
assert_serialization(IdCategory::ZsDataset, "zs_dataset");
|
||||
assert_serialization(IdCategory::ZsVoice, "zs_voice");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(IdCategory::MediaFile.to_str(), "media_file");
|
||||
assert_eq!(IdCategory::LipsyncAnimation.to_str(), "lipsync_animation");
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(IdCategory::MediaFile.to_str(), "media_file");
|
||||
assert_eq!(IdCategory::LipsyncAnimation.to_str(), "lipsync_animation");
|
||||
assert_eq!(IdCategory::VoiceConversion.to_str(), "voice_conversion");
|
||||
assert_eq!(IdCategory::ZsDataset.to_str(), "zs_dataset");
|
||||
assert_eq!(IdCategory::ZsVoice.to_str(), "zs_voice");
|
||||
assert_eq!(IdCategory::ZeroShotTTS.to_str(),"zs_audio_tts");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(IdCategory::from_str("media_file").unwrap(), IdCategory::MediaFile);
|
||||
assert_eq!(IdCategory::from_str("lipsync_animation").unwrap(), IdCategory::LipsyncAnimation);
|
||||
assert_eq!(IdCategory::from_str("voice_conversion").unwrap(), IdCategory::VoiceConversion);
|
||||
assert_eq!(IdCategory::from_str("zs_dataset").unwrap(), IdCategory::ZsDataset);
|
||||
assert_eq!(IdCategory::from_str("zs_voice").unwrap(), IdCategory::ZsVoice);
|
||||
assert_eq!(IdCategory::from_str("zs_audio_tts").unwrap(), IdCategory::ZeroShotTTS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
// Static check
|
||||
let mut variants = IdCategory::all_variants();
|
||||
assert_eq!(variants.len(), 5);
|
||||
assert_eq!(variants.pop_first(), Some(IdCategory::MediaFile));
|
||||
assert_eq!(variants.pop_first(), Some(IdCategory::LipsyncAnimation));
|
||||
assert_eq!(variants.pop_first(), Some(IdCategory::VoiceConversion));
|
||||
assert_eq!(variants.pop_first(), Some(IdCategory::ZsDataset));
|
||||
assert_eq!(variants.pop_first(), Some(IdCategory::ZsVoice));
|
||||
assert_eq!(variants.pop_first(), Some(IdCategory::ZeroShotTTS));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
|
||||
// Generated check
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(IdCategory::all_variants().len(), IdCategory::iter().len());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(IdCategory::from_str("media_file").unwrap(), IdCategory::MediaFile);
|
||||
assert_eq!(IdCategory::from_str("lipsync_animation").unwrap(), IdCategory::LipsyncAnimation);
|
||||
}
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
// Static check
|
||||
let mut variants = IdCategory::all_variants();
|
||||
assert_eq!(variants.len(), 2);
|
||||
assert_eq!(variants.pop_first(), Some(IdCategory::MediaFile));
|
||||
assert_eq!(variants.pop_first(), Some(IdCategory::LipsyncAnimation));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(IdCategory::all_variants().len(), IdCategory::iter().len());
|
||||
}
|
||||
|
||||
// Generated check
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(IdCategory::all_variants().len(), IdCategory::iter().len());
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in IdCategory::all_variants() {
|
||||
assert_eq!(variant, IdCategory::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, IdCategory::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, IdCategory::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::BTreeSet;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
@@ -10,8 +10,8 @@ use strum::EnumIter;
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MediaFileOriginCategory {
|
||||
/// ML model inference output - uploaded models or zero shot.
|
||||
Inference,
|
||||
@@ -19,8 +19,11 @@ pub enum MediaFileOriginCategory {
|
||||
/// Processed results - (we don't have these systems yet, but eg. trim, transcode, etc).
|
||||
Processed,
|
||||
|
||||
/// User uploaded files
|
||||
/// User uploaded files (from their filesystem)
|
||||
Upload,
|
||||
|
||||
/// User uploaded files recorded directly from their device (browser, mobile), typically using device APIs.
|
||||
DeviceApi,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
@@ -34,6 +37,7 @@ impl MediaFileOriginCategory {
|
||||
Self::Inference => "inference",
|
||||
Self::Processed => "processed",
|
||||
Self::Upload => "upload",
|
||||
Self::DeviceApi => "device_api",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +46,21 @@ impl MediaFileOriginCategory {
|
||||
"inference" => Ok(Self::Inference),
|
||||
"processed" => Ok(Self::Processed),
|
||||
"upload" => Ok(Self::Upload),
|
||||
"device_api" => Ok(Self::DeviceApi),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants() -> BTreeSet<Self> {
|
||||
// NB: BTreeSet is sorted
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::Inference,
|
||||
Self::Processed,
|
||||
Self::Upload,
|
||||
Self::DeviceApi,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -52,25 +68,62 @@ mod tests {
|
||||
use crate::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(MediaFileOriginCategory::Inference, "inference");
|
||||
assert_serialization(MediaFileOriginCategory::Processed, "processed");
|
||||
assert_serialization(MediaFileOriginCategory::Upload, "upload");
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(MediaFileOriginCategory::Inference, "inference");
|
||||
assert_serialization(MediaFileOriginCategory::Processed, "processed");
|
||||
assert_serialization(MediaFileOriginCategory::Upload, "upload");
|
||||
assert_serialization(MediaFileOriginCategory::DeviceApi, "device_api");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_str() {
|
||||
assert_eq!(MediaFileOriginCategory::Inference.to_str(), "inference");
|
||||
assert_eq!(MediaFileOriginCategory::Processed.to_str(), "processed");
|
||||
assert_eq!(MediaFileOriginCategory::Upload.to_str(), "upload");
|
||||
assert_eq!(MediaFileOriginCategory::DeviceApi.to_str(), "device_api");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str() {
|
||||
assert_eq!(MediaFileOriginCategory::from_str("inference").unwrap(), MediaFileOriginCategory::Inference);
|
||||
assert_eq!(MediaFileOriginCategory::from_str("processed").unwrap(), MediaFileOriginCategory::Processed);
|
||||
assert_eq!(MediaFileOriginCategory::from_str("upload").unwrap(), MediaFileOriginCategory::Upload);
|
||||
assert_eq!(MediaFileOriginCategory::from_str("device_api").unwrap(), MediaFileOriginCategory::DeviceApi);
|
||||
assert!(MediaFileOriginCategory::from_str("foo").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = MediaFileOriginCategory::all_variants();
|
||||
assert_eq!(variants.len(), 4);
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginCategory::Inference));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginCategory::Processed));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginCategory::Upload));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginCategory::DeviceApi));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_str() {
|
||||
assert_eq!(MediaFileOriginCategory::Inference.to_str(), "inference");
|
||||
assert_eq!(MediaFileOriginCategory::Processed.to_str(), "processed");
|
||||
assert_eq!(MediaFileOriginCategory::Upload.to_str(), "upload");
|
||||
}
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_from_str() {
|
||||
assert_eq!(MediaFileOriginCategory::from_str("inference").unwrap(), MediaFileOriginCategory::Inference);
|
||||
assert_eq!(MediaFileOriginCategory::from_str("processed").unwrap(), MediaFileOriginCategory::Processed);
|
||||
assert_eq!(MediaFileOriginCategory::from_str("upload").unwrap(), MediaFileOriginCategory::Upload);
|
||||
assert!(MediaFileOriginCategory::from_str("foo").is_err());
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(MediaFileOriginCategory::all_variants().len(), MediaFileOriginCategory::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in MediaFileOriginCategory::all_variants() {
|
||||
assert_eq!(variant, MediaFileOriginCategory::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, MediaFileOriginCategory::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, MediaFileOriginCategory::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
@@ -10,11 +11,21 @@ use strum::EnumIter;
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MediaFileOriginModelType {
|
||||
/// RVC (v2) voice conversion models
|
||||
#[serde(rename = "rvc_v2")]
|
||||
RvcV2,
|
||||
|
||||
/// SadTalker -- v1, we may add another enum value for future versions
|
||||
SadTalker,
|
||||
|
||||
/// so-vits-svc voice conversion models
|
||||
#[serde(rename = "so_vits_svc")]
|
||||
SoVitsSvc,
|
||||
#[serde(rename = "vall_e_x")]
|
||||
VallEX,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
@@ -25,16 +36,33 @@ impl_mysql_enum_coders!(MediaFileOriginModelType);
|
||||
impl MediaFileOriginModelType {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::RvcV2 => "rvc_v2",
|
||||
Self::SadTalker => "sad_talker",
|
||||
Self::SoVitsSvc => "so_vits_svc",
|
||||
Self::VallEX => "vall_e_x"
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(value: &str) -> Result<Self, String> {
|
||||
match value {
|
||||
"rvc_v2" => Ok(Self::RvcV2),
|
||||
"sad_talker" => Ok(Self::SadTalker),
|
||||
"so_vits_svc" => Ok(Self::SoVitsSvc),
|
||||
"vall_e_x" => Ok(Self::VallEX),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants() -> BTreeSet<Self> {
|
||||
// NB: BTreeSet is sorted
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::RvcV2,
|
||||
Self::SadTalker,
|
||||
Self::SoVitsSvc,
|
||||
Self::VallEX
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -42,19 +70,60 @@ mod tests {
|
||||
use crate::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(MediaFileOriginModelType::SadTalker, "sad_talker");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_str() {
|
||||
assert_eq!(MediaFileOriginModelType::SadTalker.to_str(), "sad_talker");
|
||||
#[test]
|
||||
fn test_to_str() {
|
||||
assert_eq!(MediaFileOriginModelType::RvcV2.to_str(), "rvc_v2");
|
||||
assert_eq!(MediaFileOriginModelType::SadTalker.to_str(), "sad_talker");
|
||||
assert_eq!(MediaFileOriginModelType::SoVitsSvc.to_str(), "so_vits_svc");
|
||||
assert_eq!(MediaFileOriginModelType::VallEX.to_str(), "vall_e_x");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str() {
|
||||
assert_eq!(MediaFileOriginModelType::from_str("sad_talker").unwrap(), MediaFileOriginModelType::SadTalker);
|
||||
#[test]
|
||||
fn test_from_str() {
|
||||
assert_eq!(MediaFileOriginModelType::from_str("rvc_v2").unwrap(), MediaFileOriginModelType::RvcV2);
|
||||
assert_eq!(MediaFileOriginModelType::from_str("sad_talker").unwrap(), MediaFileOriginModelType::SadTalker);
|
||||
assert_eq!(MediaFileOriginModelType::from_str("so_vits_svc").unwrap(), MediaFileOriginModelType::SoVitsSvc);
|
||||
assert_eq!(MediaFileOriginModelType::from_str("vall_e_x").unwrap(), MediaFileOriginModelType::VallEX);
|
||||
assert!(MediaFileOriginModelType::from_str("foo").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = MediaFileOriginModelType::all_variants();
|
||||
assert_eq!(variants.len(), 4);
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::RvcV2));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::SadTalker));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::SoVitsSvc));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginModelType::VallEX));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(MediaFileOriginModelType::all_variants().len(), MediaFileOriginModelType::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in MediaFileOriginModelType::all_variants() {
|
||||
assert_eq!(variant, MediaFileOriginModelType::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, MediaFileOriginModelType::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, MediaFileOriginModelType::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
|
||||
/// Used in the `media_files` table in `VARCHAR(16)` field `origin_product_category`.
|
||||
///
|
||||
/// This value indicates what product originally created the media file. (Not the ML model or
|
||||
/// user upload process.) This will let us scope media files to the product that generated them
|
||||
/// and filter them out of unrelated products if necessary (eg. a user probably doesn't want
|
||||
/// "Voice Designer" dataset samples in a video generation flow.)
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
pub enum MediaFileOriginProductCategory {
|
||||
/// Media files created by (or uploaded for) the Face Animator product.
|
||||
/// The underlying model could be SadTalker, Wav2Lip, or some future model
|
||||
#[serde(rename = "face_animator")]
|
||||
FaceAnimator,
|
||||
|
||||
// TODO: This should be a temporary category until we migrate the DB to remove this default value
|
||||
/// Unknown which product is attached to the file (generated the file, the file was
|
||||
/// uploaded on behalf of, etc.)
|
||||
#[serde(rename = "unknown")]
|
||||
Unknown,
|
||||
|
||||
/// Voice conversion (either RVC or SVC)
|
||||
#[serde(rename = "voice_conversion")]
|
||||
VoiceConversion,
|
||||
|
||||
/// Media files created by (or uploaded for) the Zero Shot voice product.
|
||||
#[serde(rename = "zs_voice")]
|
||||
ZeroShotVoice,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
impl_enum_display_and_debug_using_to_str!(MediaFileOriginProductCategory);
|
||||
impl_mysql_enum_coders!(MediaFileOriginProductCategory);
|
||||
|
||||
/// NB: Legacy API for older code.
|
||||
impl MediaFileOriginProductCategory {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::FaceAnimator => "face_animator",
|
||||
Self::Unknown => "unknown",
|
||||
Self::VoiceConversion => "voice_conversion",
|
||||
Self::ZeroShotVoice => "zs_voice",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(value: &str) -> Result<Self, String> {
|
||||
match value {
|
||||
"face_animator" => Ok(Self::FaceAnimator),
|
||||
"unknown" => Ok(Self::Unknown),
|
||||
"voice_conversion" => Ok(Self::VoiceConversion),
|
||||
"zs_voice" => Ok(Self::ZeroShotVoice),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants() -> BTreeSet<Self> {
|
||||
// NB: BTreeSet is sorted
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::FaceAnimator,
|
||||
Self::Unknown,
|
||||
Self::VoiceConversion,
|
||||
Self::ZeroShotVoice,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(MediaFileOriginProductCategory::FaceAnimator, "face_animator");
|
||||
assert_serialization(MediaFileOriginProductCategory::Unknown, "unknown");
|
||||
assert_serialization(MediaFileOriginProductCategory::VoiceConversion, "voice_conversion");
|
||||
assert_serialization(MediaFileOriginProductCategory::ZeroShotVoice, "zs_voice");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(MediaFileOriginProductCategory::FaceAnimator.to_str(), "face_animator");
|
||||
assert_eq!(MediaFileOriginProductCategory::Unknown.to_str(), "unknown");
|
||||
assert_eq!(MediaFileOriginProductCategory::VoiceConversion.to_str(), "voice_conversion");
|
||||
assert_eq!(MediaFileOriginProductCategory::ZeroShotVoice.to_str(), "zs_voice");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(MediaFileOriginProductCategory::from_str("face_animator").unwrap(), MediaFileOriginProductCategory::FaceAnimator);
|
||||
assert_eq!(MediaFileOriginProductCategory::from_str("unknown").unwrap(), MediaFileOriginProductCategory::Unknown);
|
||||
assert_eq!(MediaFileOriginProductCategory::from_str("voice_conversion").unwrap(), MediaFileOriginProductCategory::VoiceConversion);
|
||||
assert_eq!(MediaFileOriginProductCategory::from_str("zs_voice").unwrap(), MediaFileOriginProductCategory::ZeroShotVoice);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = MediaFileOriginProductCategory::all_variants();
|
||||
assert_eq!(variants.len(), 4);
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::FaceAnimator));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::Unknown));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::VoiceConversion));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::ZeroShotVoice));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(MediaFileOriginProductCategory::all_variants().len(), MediaFileOriginProductCategory::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in MediaFileOriginProductCategory::all_variants() {
|
||||
assert_eq!(variant, MediaFileOriginProductCategory::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, MediaFileOriginProductCategory::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, MediaFileOriginProductCategory::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod media_file_origin_category;
|
||||
pub mod media_file_origin_model_type;
|
||||
pub mod media_file_origin_product_category;
|
||||
pub mod media_file_type;
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
@@ -62,8 +61,8 @@ impl TtsRenderStatus {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::test_helpers::assert_serialization;
|
||||
use crate::by_table::tts_render_tasks::tts_render_status::TtsRenderStatus;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
mod serde {
|
||||
use super::*;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
-1
@@ -5,7 +5,6 @@ use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
|
||||
|
||||
/// Used in the `voice_conversion_models` table in `VARCHAR(32)` field `model_type`.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
|
||||
-1
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
|
||||
/// This is used in nearly every job system as an actual MySQL ENUM value:
|
||||
///
|
||||
/// - tts_download_job
|
||||
/// - tts_inference_job
|
||||
/// - w2l_download_job
|
||||
/// - w2l_inference_job
|
||||
/// - generic_download_job
|
||||
/// - (NOT generic_inference_job, which uses JobStatusPlus)
|
||||
///
|
||||
/// See the documentation on the table for usage.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, sqlx::Type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[sqlx(rename_all = "snake_case")]
|
||||
pub enum JobStatus {
|
||||
Pending,
|
||||
Started,
|
||||
CompleteSuccess,
|
||||
CompleteFailure,
|
||||
AttemptFailed,
|
||||
Dead,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
impl_enum_display_and_debug_using_to_str!(JobStatus);
|
||||
|
||||
// NB: We can derive `sqlx::Type` instead of using `impl_mysql_enum_coders`
|
||||
|
||||
impl JobStatus {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Pending => "pending",
|
||||
Self::Started => "started",
|
||||
Self::CompleteSuccess => "complete_success",
|
||||
Self::CompleteFailure => "complete_failure",
|
||||
Self::AttemptFailed => "attempt_failed",
|
||||
Self::Dead => "dead",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(job_status: &str) -> Result<Self, String> {
|
||||
match job_status {
|
||||
"pending" => Ok(Self::Pending),
|
||||
"started" => Ok(Self::Started),
|
||||
"complete_success" => Ok(Self::CompleteSuccess),
|
||||
"complete_failure" => Ok(Self::CompleteFailure),
|
||||
"attempt_failed" => Ok(Self::AttemptFailed),
|
||||
"dead" => Ok(Self::Dead),
|
||||
_ => Err(format!("invalid job_status: {:?}", job_status)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants() -> BTreeSet<Self> {
|
||||
// NB: BTreeSet is sorted
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::Pending,
|
||||
Self::Started,
|
||||
Self::CompleteSuccess,
|
||||
Self::CompleteFailure,
|
||||
Self::AttemptFailed,
|
||||
Self::Dead,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::common::job_status::JobStatus;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(JobStatus::Pending, "pending");
|
||||
assert_serialization(JobStatus::Started, "started");
|
||||
assert_serialization(JobStatus::CompleteSuccess, "complete_success");
|
||||
assert_serialization(JobStatus::CompleteFailure, "complete_failure");
|
||||
assert_serialization(JobStatus::AttemptFailed, "attempt_failed");
|
||||
assert_serialization(JobStatus::Dead, "dead");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(JobStatus::Pending.to_str(), "pending");
|
||||
assert_eq!(JobStatus::Started.to_str(), "started");
|
||||
assert_eq!(JobStatus::CompleteSuccess.to_str(), "complete_success");
|
||||
assert_eq!(JobStatus::CompleteFailure.to_str(), "complete_failure");
|
||||
assert_eq!(JobStatus::AttemptFailed.to_str(), "attempt_failed");
|
||||
assert_eq!(JobStatus::Dead.to_str(), "dead");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(JobStatus::from_str("pending").unwrap(), JobStatus::Pending);
|
||||
assert_eq!(JobStatus::from_str("started").unwrap(), JobStatus::Started);
|
||||
assert_eq!(JobStatus::from_str("complete_success").unwrap(), JobStatus::CompleteSuccess);
|
||||
assert_eq!(JobStatus::from_str("complete_failure").unwrap(), JobStatus::CompleteFailure);
|
||||
assert_eq!(JobStatus::from_str("attempt_failed").unwrap(), JobStatus::AttemptFailed);
|
||||
assert_eq!(JobStatus::from_str("dead").unwrap(), JobStatus::Dead);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = JobStatus::all_variants();
|
||||
assert_eq!(variants.len(), 6);
|
||||
assert_eq!(variants.pop_first(), Some(JobStatus::Pending));
|
||||
assert_eq!(variants.pop_first(), Some(JobStatus::Started));
|
||||
assert_eq!(variants.pop_first(), Some(JobStatus::CompleteSuccess));
|
||||
assert_eq!(variants.pop_first(), Some(JobStatus::CompleteFailure));
|
||||
assert_eq!(variants.pop_first(), Some(JobStatus::AttemptFailed));
|
||||
assert_eq!(variants.pop_first(), Some(JobStatus::Dead));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(JobStatus::all_variants().len(), JobStatus::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in JobStatus::all_variants() {
|
||||
assert_eq!(variant, JobStatus::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, JobStatus::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, JobStatus::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
|
||||
/// This is used in newer jobs (that add additional enum states)
|
||||
///
|
||||
/// - generic_inference_job
|
||||
/// - (no other jobs yet)
|
||||
///
|
||||
/// See the documentation on the table for usage.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, sqlx::Type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[sqlx(rename_all = "snake_case")]
|
||||
pub enum JobStatusPlus {
|
||||
Pending,
|
||||
Started,
|
||||
CompleteSuccess,
|
||||
CompleteFailure,
|
||||
AttemptFailed,
|
||||
Dead,
|
||||
CancelledByUser,
|
||||
CancelledBySystem,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
impl_enum_display_and_debug_using_to_str!(JobStatusPlus);
|
||||
|
||||
// NB: We can derive `sqlx::Type` instead of using `impl_mysql_enum_coders`
|
||||
|
||||
impl JobStatusPlus {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Pending => "pending",
|
||||
Self::Started => "started",
|
||||
Self::CompleteSuccess => "complete_success",
|
||||
Self::CompleteFailure => "complete_failure",
|
||||
Self::AttemptFailed => "attempt_failed",
|
||||
Self::Dead => "dead",
|
||||
Self::CancelledByUser => "cancelled_by_user",
|
||||
Self::CancelledBySystem => "cancelled_by_system",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(job_status: &str) -> Result<Self, String> {
|
||||
match job_status {
|
||||
"pending" => Ok(Self::Pending),
|
||||
"started" => Ok(Self::Started),
|
||||
"complete_success" => Ok(Self::CompleteSuccess),
|
||||
"complete_failure" => Ok(Self::CompleteFailure),
|
||||
"attempt_failed" => Ok(Self::AttemptFailed),
|
||||
"dead" => Ok(Self::Dead),
|
||||
"cancelled_by_user" => Ok(Self::CancelledByUser),
|
||||
"cancelled_by_system" => Ok(Self::CancelledBySystem),
|
||||
_ => Err(format!("invalid job_status: {:?}", job_status)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants() -> BTreeSet<Self> {
|
||||
// NB: BTreeSet is sorted
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::Pending,
|
||||
Self::Started,
|
||||
Self::CompleteSuccess,
|
||||
Self::CompleteFailure,
|
||||
Self::AttemptFailed,
|
||||
Self::Dead,
|
||||
Self::CancelledByUser,
|
||||
Self::CancelledBySystem,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::common::job_status_plus::JobStatusPlus;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(JobStatusPlus::Pending, "pending");
|
||||
assert_serialization(JobStatusPlus::Started, "started");
|
||||
assert_serialization(JobStatusPlus::CompleteSuccess, "complete_success");
|
||||
assert_serialization(JobStatusPlus::CompleteFailure, "complete_failure");
|
||||
assert_serialization(JobStatusPlus::AttemptFailed, "attempt_failed");
|
||||
assert_serialization(JobStatusPlus::Dead, "dead");
|
||||
assert_serialization(JobStatusPlus::CancelledByUser, "cancelled_by_user");
|
||||
assert_serialization(JobStatusPlus::CancelledBySystem, "cancelled_by_system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(JobStatusPlus::Pending.to_str(), "pending");
|
||||
assert_eq!(JobStatusPlus::Started.to_str(), "started");
|
||||
assert_eq!(JobStatusPlus::CompleteSuccess.to_str(), "complete_success");
|
||||
assert_eq!(JobStatusPlus::CompleteFailure.to_str(), "complete_failure");
|
||||
assert_eq!(JobStatusPlus::AttemptFailed.to_str(), "attempt_failed");
|
||||
assert_eq!(JobStatusPlus::Dead.to_str(), "dead");
|
||||
assert_eq!(JobStatusPlus::CancelledByUser.to_str(), "cancelled_by_user");
|
||||
assert_eq!(JobStatusPlus::CancelledBySystem.to_str(), "cancelled_by_system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(JobStatusPlus::from_str("pending").unwrap(), JobStatusPlus::Pending);
|
||||
assert_eq!(JobStatusPlus::from_str("started").unwrap(), JobStatusPlus::Started);
|
||||
assert_eq!(JobStatusPlus::from_str("complete_success").unwrap(), JobStatusPlus::CompleteSuccess);
|
||||
assert_eq!(JobStatusPlus::from_str("complete_failure").unwrap(), JobStatusPlus::CompleteFailure);
|
||||
assert_eq!(JobStatusPlus::from_str("attempt_failed").unwrap(), JobStatusPlus::AttemptFailed);
|
||||
assert_eq!(JobStatusPlus::from_str("dead").unwrap(), JobStatusPlus::Dead);
|
||||
assert_eq!(JobStatusPlus::from_str("cancelled_by_user").unwrap(), JobStatusPlus::CancelledByUser);
|
||||
assert_eq!(JobStatusPlus::from_str("cancelled_by_system").unwrap(), JobStatusPlus::CancelledBySystem);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = JobStatusPlus::all_variants();
|
||||
assert_eq!(variants.len(), 8);
|
||||
assert_eq!(variants.pop_first(), Some(JobStatusPlus::Pending));
|
||||
assert_eq!(variants.pop_first(), Some(JobStatusPlus::Started));
|
||||
assert_eq!(variants.pop_first(), Some(JobStatusPlus::CompleteSuccess));
|
||||
assert_eq!(variants.pop_first(), Some(JobStatusPlus::CompleteFailure));
|
||||
assert_eq!(variants.pop_first(), Some(JobStatusPlus::AttemptFailed));
|
||||
assert_eq!(variants.pop_first(), Some(JobStatusPlus::Dead));
|
||||
assert_eq!(variants.pop_first(), Some(JobStatusPlus::CancelledByUser));
|
||||
assert_eq!(variants.pop_first(), Some(JobStatusPlus::CancelledBySystem));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(JobStatusPlus::all_variants().len(), JobStatusPlus::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in JobStatusPlus::all_variants() {
|
||||
// Test to_str(), from_str(), Display, and Debug.
|
||||
assert_eq!(variant, JobStatusPlus::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, JobStatusPlus::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, JobStatusPlus::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
pub mod job_status;
|
||||
pub mod job_status_plus;
|
||||
pub mod sqlite;
|
||||
pub mod visibility;
|
||||
pub mod vocoder_type;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
@@ -83,8 +82,8 @@ impl WebContentType {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::test_helpers::assert_serialization;
|
||||
use crate::common::sqlite::web_content_type::WebContentType;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
mod serde {
|
||||
use super::*;
|
||||
|
||||
@@ -144,6 +144,7 @@ mod tests {
|
||||
|
||||
mod serde_serialization {
|
||||
use crate::common::visibility::Visibility;
|
||||
|
||||
use super::CompositeType;
|
||||
|
||||
#[test]
|
||||
@@ -166,6 +167,7 @@ mod tests {
|
||||
|
||||
mod serde_deserialization {
|
||||
use crate::common::visibility::Visibility;
|
||||
|
||||
use super::CompositeType;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -18,7 +18,7 @@ path = "src/lib.rs"
|
||||
# (none)
|
||||
|
||||
# External
|
||||
anyhow = "1.0.40"
|
||||
anyhow = "1.0.75"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
|
||||
# Serialization
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
pub mod media_upload;
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod download;
|
||||
pub mod inference;
|
||||
@@ -1,8 +1,20 @@
|
||||
//! tokens
|
||||
//!
|
||||
//! The purpose of this library is to have a strongly-typed primary/foreign key system.
|
||||
//! Every Database or Redis key will have a type here.
|
||||
//! Well known keys will have short identifiers (eg. "user" is prefixed with "U:")
|
||||
//! The purpose of this library is to provide a strongly-typed primary/foreign key system with
|
||||
//! well-known identifier prefixes to aid in manual database debugging.
|
||||
//!
|
||||
//! 1) All table foreign keys have a strongly-typed (non-stringly typed) wrapper type,
|
||||
//! eg. `MediaFileToken` for the `token` field in the `media_files` table or `UserToken` for
|
||||
//! the `token` field in the `users` table.
|
||||
//!
|
||||
//! 2) All such keys should have a well-defined constant prefix. Though tokens should be 100%
|
||||
//! opaque to the user and the code, having well-defined prefixes can greatly aid when
|
||||
//! debugging systems. Every table's primary key should have a unique prefix that identifies
|
||||
//! that key, eg. `comment_` for the `comments` table or `m_` for the `media_files` table.
|
||||
//!
|
||||
//! This library does not directly introduce MySQL dependencies, so these strongly-typed token
|
||||
//! types can be used in other crates, eg. for defining HTTP APIs and returning strongly-typed
|
||||
//! tokens to the user.
|
||||
//!
|
||||
|
||||
// Never allow these
|
||||
@@ -39,8 +51,4 @@ mod macros;
|
||||
|
||||
pub (crate) mod prefixes;
|
||||
|
||||
pub mod files;
|
||||
pub mod jobs;
|
||||
pub mod tokens;
|
||||
pub mod users;
|
||||
pub mod voice_conversion;
|
||||
|
||||
@@ -13,6 +13,8 @@ use strum::EnumIter;
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
pub(crate) enum EntityType {
|
||||
/// AVTs are not stored as primary keys in any table, but an index in many tables.
|
||||
AnonymousVisitorTracking,
|
||||
AuditLog,
|
||||
Comment,
|
||||
DownloadJob,
|
||||
@@ -24,15 +26,20 @@ pub(crate) enum EntityType {
|
||||
TtsModel,
|
||||
TtsRenderTask, // NB: aichatbot / sqlite
|
||||
User,
|
||||
UserDeprecatedDoNotUse, // NB: Users prior to 2023-10-24 used this prefix.
|
||||
VoiceConversionModel,
|
||||
VoiceConversionResult,
|
||||
W2lTemplate,
|
||||
ZsVoice,
|
||||
ZsVoiceDataset,
|
||||
ZsVoiceDatasetSample,
|
||||
}
|
||||
|
||||
impl EntityType {
|
||||
|
||||
pub fn prefix(self) -> &'static str {
|
||||
match self {
|
||||
Self::AnonymousVisitorTracking => "avt_",
|
||||
Self::AuditLog => "audit_",
|
||||
Self::Comment => "comment_",
|
||||
Self::DownloadJob => "jdown_", // NB: Was "JGUP:"
|
||||
@@ -43,21 +50,27 @@ impl EntityType {
|
||||
Self::NewsStory => "news_story_",
|
||||
Self::TtsModel => "TM:", // NB: Old-style prefix, do not use for future tokens.
|
||||
Self::TtsRenderTask => "tts_task_",
|
||||
Self::User => "U:", // NB: Old-style prefix, do not use for future tokens.
|
||||
Self::User => "user_",
|
||||
Self::UserDeprecatedDoNotUse => "U:", // NB: Users prior to 2023-10-24 used this prefix.
|
||||
Self::VoiceConversionModel => "vcm_",
|
||||
Self::VoiceConversionResult => "vcr_",
|
||||
Self::W2lTemplate => "WT:", // NB: Old-style prefix, do not use for future tokens.
|
||||
Self::ZsVoice => "zsv_",
|
||||
Self::ZsVoiceDataset => "zsd_",
|
||||
Self::ZsVoiceDatasetSample => "zss_",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::prefixes::EntityType;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use strum::EnumCount;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::prefixes::EntityType;
|
||||
|
||||
#[test]
|
||||
fn test_all_prefixes_are_unique() {
|
||||
let entities = EntityType::iter()
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::prefixes::EntityType;
|
||||
|
||||
/// A rare instance that is *not* a primary key.
|
||||
/// These are generated as cookies to track users for a better logged out experience and for analytics.
|
||||
/// We use these as indices into several columns (ML result types, uploads, etc.)
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::Type, Debug, Serialize, Deserialize)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct AnonymousVisitorTrackingToken(pub String);
|
||||
|
||||
impl_string_token!(AnonymousVisitorTrackingToken);
|
||||
impl_crockford_generator!(AnonymousVisitorTrackingToken, 32usize, EntityType::AnonymousVisitorTracking, CrockfordLower);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user