From 72d6c0c528e86f3d3f9df2f6f992b3f7c4fcc248 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Wed, 10 Aug 2022 05:33:57 -0400 Subject: [PATCH] UI for editing custom vocoders in TTS models --- packages/components/src/api/ApiConfig.ts | 8 ++ .../src/api/vocoder/ListVocoderModels.ts | 71 ++++++++++++++++++ .../tts/tts_model_edit/TtsModelEditPage.tsx | 74 +++++++++++++++++-- 3 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 packages/components/src/api/vocoder/ListVocoderModels.ts diff --git a/packages/components/src/api/ApiConfig.ts b/packages/components/src/api/ApiConfig.ts index bf5e82de7f..182113f5a8 100644 --- a/packages/components/src/api/ApiConfig.ts +++ b/packages/components/src/api/ApiConfig.ts @@ -284,6 +284,14 @@ class ApiConfig { return base_url + query; } + listVocoderModels(): string { + return `${this.getScheme()}://${this.getNewApiHost()}/vocoder/list`; + } + + getVocoderModel(vocoderToken: string): string { + return `${this.getScheme()}://${this.getNewApiHost()}/vocoder/model/${vocoderToken}`; + } + createCategory(): string { return `${this.getScheme()}://${this.getNewApiHost()}/category/create`; } diff --git a/packages/components/src/api/vocoder/ListVocoderModels.ts b/packages/components/src/api/vocoder/ListVocoderModels.ts new file mode 100644 index 0000000000..e1c6e93ad6 --- /dev/null +++ b/packages/components/src/api/vocoder/ListVocoderModels.ts @@ -0,0 +1,71 @@ + +import { ApiConfig } from "../ApiConfig"; + +export interface ListVocoderModelsSuccessResponse { + success: boolean, + vocoders: Array +} + +export interface VocoderModel { + vocoder_token: string, + vocoder_type: string, + title: string, + is_staff_recommended: boolean, + creator_username: string, + creator_display_name: string, + creator_gravatar_hash: string, + created_at: Date, + updated_at: Date, + moderator_fields: VocoderModelModeratorFields | undefined | null, +} + +export interface VocoderModelModeratorFields { + is_mod_disabled_from_public_use: boolean, + is_mod_disabled_from_author_use: boolean, + is_mod_author_editing_locked: boolean, + mod_deleted_at: string | undefined | null, + user_deleted_at: string | undefined | null, +} + + +export interface ListVocoderModelsErrorResponse { + success: boolean, +} + +type ListVocoderModelsResponse = ListVocoderModelsSuccessResponse | ListVocoderModelsErrorResponse; + +export function ListVocoderModelsIsOk(response: ListVocoderModelsResponse): response is ListVocoderModelsSuccessResponse { + return response?.success === true; +} + +export function ListVocoderModelsIsError(response: ListVocoderModelsResponse): response is ListVocoderModelsErrorResponse { + return response?.success === false; +} + +export async function ListVocoderModels() : Promise +{ + const endpoint = new ApiConfig().listVocoderModels(); + + return await fetch(endpoint, { + method: 'GET', + headers: { + 'Accept': 'application/json', + }, + credentials: 'include', + }) + .then(res => res.json()) + .then(res => { + if (!res) { + return { success : false }; // TODO: This loses error semantics and is deprecated + } + + if (res && 'success' in res) { + return res; + } else { + return { success : false }; + } + }) + .catch(e => { + return { success : false }; + }); +} diff --git a/packages/fakeyou/src/v2/view/tts/tts_model_edit/TtsModelEditPage.tsx b/packages/fakeyou/src/v2/view/tts/tts_model_edit/TtsModelEditPage.tsx index 855e464f20..826b8312a2 100644 --- a/packages/fakeyou/src/v2/view/tts/tts_model_edit/TtsModelEditPage.tsx +++ b/packages/fakeyou/src/v2/view/tts/tts_model_edit/TtsModelEditPage.tsx @@ -13,6 +13,11 @@ import { TtsModel, TtsModelLookupError, } from "@storyteller/components/src/api/tts/GetTtsModel"; +import { + ListVocoderModels, + ListVocoderModelsIsOk, + VocoderModel, +} from "@storyteller/components/src/api/vocoder/ListVocoderModels"; import { BackLink } from "../../_common/BackLink"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { @@ -51,20 +56,28 @@ function TtsModelEditPage(props: Props) { const [ttsModel, setTtsModel] = useState(undefined); const [notFoundState, setNotFoundState] = useState(false); + // Vocoder lookup + const [vocoderModels, setVocoderModels] = useState([]); + // Fields const [title, setTitle] = useState(""); const [textPipelineType, setTextPipelineType] = useState(null); const [descriptionMarkdown, setDescriptionMarkdown] = useState(""); const [fullLanguageTag, setFullLanguageTag] = useState(""); // NB: Should be full IETF, eg. ["en", "en-US", "es-419", etc.] const [visibility, setVisibility] = useState(DEFAULT_VISIBILITY); - const [defaultPretrainedVocoder, setDefaultPretrainedVocoder] = useState( - DEFAULT_PRETRAINED_VOCODER - ); const [isFrontPageFeatured, setIsFrontPageFeatured] = useState(false); const [isTwitchFeatured, setIsTwitchFeatured] = useState(false); const [suggestedUniqueBotCommand, setSuggestedUniqueBotCommand] = useState(""); + // Vocoder config (modern) + const [maybeCustomVocoderToken, setMaybeCustomVocoderToken] = useState(null); + + // Vocoder config (legacy) + const [defaultPretrainedVocoder, setDefaultPretrainedVocoder] = useState( + DEFAULT_PRETRAINED_VOCODER + ); + const getModel = useCallback(async (token) => { const model = await GetTtsModel(token); @@ -81,14 +94,17 @@ function TtsModelEditPage(props: Props) { setDescriptionMarkdown(model.description_markdown || ""); setFullLanguageTag(model.ietf_language_tag || DEFAULT_MODEL_LANGUAGE); setVisibility(model.creator_set_visibility || DEFAULT_VISIBILITY); - setDefaultPretrainedVocoder( - model.maybe_default_pretrained_vocoder || DEFAULT_PRETRAINED_VOCODER - ); setIsFrontPageFeatured(model.is_front_page_featured || false); setIsTwitchFeatured(model.is_twitch_featured || false); setSuggestedUniqueBotCommand( model.maybe_suggested_unique_bot_command || "" ); + if (!!model.maybe_custom_vocoder) { + setMaybeCustomVocoderToken(model.maybe_custom_vocoder.vocoder_token); + } + setDefaultPretrainedVocoder( + model.maybe_default_pretrained_vocoder || DEFAULT_PRETRAINED_VOCODER + ); } else if (GetTtsModelIsErr(model)) { switch (model) { case TtsModelLookupError.NotFound: @@ -98,9 +114,20 @@ function TtsModelEditPage(props: Props) { } }, []); + + const listVocoderModels = useCallback(async () => { + const models = await ListVocoderModels(); + + if (ListVocoderModelsIsOk(models)) { + setVocoderModels(models.vocoders); + } + }, []); + + useEffect(() => { getModel(token); - }, [token, getModel]); + listVocoderModels(); + }, [token, getModel, listVocoderModels]); const handleTitleChange = (ev: React.FormEvent) => { ev.preventDefault(); @@ -138,6 +165,13 @@ function TtsModelEditPage(props: Props) { setDefaultPretrainedVocoder((ev.target as HTMLSelectElement).value); }; + const handleCustomVocoderChange = ( + ev: React.FormEvent + ) => { + setMaybeCustomVocoderToken((ev.target as HTMLSelectElement).value); + }; + + const handleIsFrontPageFeaturedChange = ( ev: React.FormEvent ) => { @@ -184,6 +218,7 @@ function TtsModelEditPage(props: Props) { creator_set_visibility: visibility || DEFAULT_VISIBILITY, maybe_default_pretrained_vocoder: defaultPretrainedVocoder || DEFAULT_PRETRAINED_VOCODER, + maybe_custom_vocoder_token: maybeCustomVocoderToken, ietf_language_tag: fullLanguageTag || DEFAULT_MODEL_LANGUAGE, text_pipeline_type: textPipelineType, }; @@ -312,6 +347,17 @@ function TtsModelEditPage(props: Props) { ); } + let customVocoderOptions = vocoderModels.map((vocoder) => { + return ( + + ) + }); + let usableTextPipelines = isModerator ? TEXT_PIPELINE_NAMES_FOR_MODERATORS : TEXT_PIPELINE_NAMES; @@ -437,6 +483,20 @@ function TtsModelEditPage(props: Props) { +
+ +
+ +
+
+