code optimization

This commit is contained in:
wil committed 2024-04-22 21:42:57 -04:00
1 parent 6b667a56e7
commit c845cfa3fc
19 files changed
+57 -461

No files matched your search

+7
View File
@@ -2,6 +2,7 @@ import { Fragment, useState, useEffect } from 'react'
import { Combobox, Transition } from '@headlessui/react'
import { faCheck, faChevronDown } from '@fortawesome/pro-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { disableHotkeyInput,enableHotkeyInput,DomLevels } from "~/pages/PageEnigma/store";
interface ListDropdownProps {
list: {[key: string] : string}[];
@@ -45,6 +46,12 @@ export const ListSearchDropdown = ({
if(listDisplayKey) return item[listDisplayKey];
else return Object.values(item)[0]
}}
onFocus={(e) => {
disableHotkeyInput(DomLevels.INPUT)
}}
onBlur={(e) => {
enableHotkeyInput(DomLevels.INPUT)
}}
onChange={(event) => setQuery(event.target.value)}
/>
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
+3 -1
View File
@@ -13,6 +13,7 @@ import { ListSearchDropdown } from "./ListSearchDropdown"
import { LoadingBar } from "./LoadingBar";
import { LoadingDots, LoadingDotsBricks, LoadingDotsTyping } from "./LoadingDots";
import { Pill } from "./Pill";
import { WaveformPlayer } from "./WaveformPlayer";
export {
Button,
@@ -31,5 +32,6 @@ export {
LoadingDots,
LoadingDotsBricks,
LoadingDotsTyping,
Pill
Pill,
WaveformPlayer
};
-2
View File
@@ -25,7 +25,6 @@ import { useSignals } from "@preact/signals-react/runtime";
import { AppUiContext } from "~/contexts/AppUiContext";
import { pageHeight, pageWidth } from "~/store";
import { TopBar } from "~/modules/TopBar";
import { DialogueTTS } from "./comps/DialogueTTS/DialogueTTS";
import { Pages } from "~/pages/PageEnigma/constants/page";
import { AssetType } from "./models/assets";
@@ -133,7 +132,6 @@ export const PageEditor = ({ setPage }: Props) => {
<PreviewEngineCamera />
<ControlsVideo />
<ControlPanelSceneObject />
<DialogueTTS />
</div>
</div>
{appUiState.viewMode === APPUI_VIEW_MODES.SIDE_BY_SIDE && (
@@ -1,243 +0,0 @@
import { useCallback, useContext, useEffect, useState } from "react";
import { useSignalEffect } from "@preact/signals-react/runtime";
import { v4 as uuidv4 } from "uuid";
import {
faVolume,
faShuffle,
// faPlay,
faBrainCircuit,
} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { AppUiContext } from "~/contexts/AppUiContext";
import { APPUI_ACTION_TYPES } from "~/reducers";
import {
Button,
H2,
H5,
Label,
LoadingDotsTyping,
ListSearchDropdown,
TransitionDialogue,
Textarea
} from "~/components";
import { ListTtsModels } from "./utilities";
import { TtsModelListItem } from "./types";
import { GenerateTtsAudio } from "./generateTts";
import { addInferenceJob, inferenceJobs} from "../../store/inferenceJobs";
import { JobState } from "~/hooks/useInferenceJobManager/useInferenceJobManager";
type TtsState = {
voice: TtsModelListItem | undefined;
text: string;
hasEnqueued :boolean;
inferenceToken?: string;
inferenceJobType?: string;
hasAudio: boolean;
audioFile?: any;
}
export const DialogueTTS = ()=>{
const [appUiState, dispatchAppUiState] = useContext(AppUiContext);
const [ttsState, setTtsState] = useState<TtsState>({
voice:undefined,
text:"",
hasEnqueued:false,
hasAudio:false,
});
useSignalEffect(()=>{
console.log(inferenceJobs.value);
if(ttsState.hasEnqueued && ttsState.inferenceToken){
const found = inferenceJobs.value.find((job)=>job.job_id===ttsState.inferenceToken);
console.log(`finding: ${ttsState.inferenceToken}`);
console.log(found);
if(found?.job_status === JobState.COMPLETE_SUCCESS){
setTtsState((curr)=>({
...curr,
hasAudio: true,
audioFile: found.result
}))
}
}
});
const [ttsModels, setTtsModels] = useState<Array<TtsModelListItem>>([]);
const listModels = useCallback(async () => {
const ttsModelsLoaded = ttsModels.length > 0;
if (ttsModelsLoaded) {
return; // Already queried.
}
const models = await ListTtsModels();
if (models) {
setTtsModels(models);
}
}, []);
useEffect(() => {
listModels();
}, [listModels]);
useEffect(()=> {
if(ttsState.hasEnqueued && ttsState.inferenceToken && ttsState.inferenceJobType){
console.log(`tts has Enqueued`);
addInferenceJob({
version:1,
job_id: ttsState.inferenceToken,
job_type: ttsState.inferenceJobType,
job_status: JobState.PENDING,
})
}
},[ttsState]);
const requestTts = useCallback( ()=>{
const modelToken = ttsState.voice ? ttsState.voice.model_token : undefined;
if(modelToken){
setTtsState((curr)=>({
...curr,
hasEnqueued: true,
inferenceToken: undefined,
inferenceJobType: undefined,
hasAudio: false,
result: undefined,
}));
const request = {
uuid_idempotency_token: uuidv4(),
tts_model_token: modelToken,
inference_text: ttsState.text,
};
GenerateTtsAudio(request).then(res=>{
if(res.inference_job_token){
setTtsState((curr)=>({
...curr,
inferenceToken: res.inference_job_token,
inferenceJobType: res.inference_job_token_type
}));
}
});
}else{
console.log("no voice model selected");
}
},[ttsState]);
const handleClose = ()=> {
if(ttsState.hasAudio){
setTtsState((curr)=>({
...curr,
hasEnqueued: false,
inferenceToken: undefined,
inferenceJobType: undefined,
hasAudio: false,
result: undefined,
}));
}
dispatchAppUiState({
type: APPUI_ACTION_TYPES.CLOSE_DIALOGUE_TTS
})
};
const handleTextInput = (
e: React.ChangeEvent<HTMLTextAreaElement>
)=>{
setTtsState((curr)=>({
...curr,
text: e.target.value,
}));
};
const handleOnSelect = (val:string)=>{
const voiceModel = ttsModels.find((item)=>{
if (item.title === val) return item
})
// console.log(val);
// console.log(voiceModel);
setTtsState((curr)=>({
...curr,
voice: voiceModel,
}));
}
return(
<TransitionDialogue
title={
<>
<FontAwesomeIcon icon={faVolume} className="pr-2"/>
Generate TTS
</>
}
className="max-w-xl"
isOpen={appUiState.diagloueTts.isOpen}
onClose={handleClose}
>
<div className="flex flex-col">
<Label className="mb-1">Select a Voice</Label>
<ListSearchDropdown
list={ttsModels}
listDisplayKey="title"
onSelect={handleOnSelect}
/>
<div className="flex w-full justify-between mt-4">
<Label>What would you like to say?</Label>
<div className="flex gap-2 items-center">
<FontAwesomeIcon className="text-brand-primary" icon={faShuffle}/>
<H5 className="text-brand-primary">Randomized Text</H5>
</div>
</div>
<Textarea
placeholder="Enter what you want the voice to say here."
value={ttsState.text}
onChange={handleTextInput}
/>
<div className="mt-6 flex gap-2">
<div className="w-full h-12">
{!ttsState.hasAudio && !ttsState.hasEnqueued &&
<Button
className="w-36 h-full text-xl "
variant={ttsState.hasAudio ? "secondary" : "primary" }
disabled={ttsState.text === ""}
icon={faBrainCircuit}
onClick={requestTts}
>
Generate
</Button>
}
{!ttsState.hasAudio && ttsState.hasEnqueued &&
<LoadingDotsTyping className="bg-brand-secondary-500 rounded-lg"/>
}
{ ttsState.hasAudio &&
<div className="bg-success w-full h-full rounded-lg flex items-center justify-center">
<H2>Success!! Please Check "My Audio" in the Audio Panel.</H2>
</div>
}
</div>
</div>
<div className="mt-6 flex justify-between gap-2">
{ ttsState.hasEnqueued &&
<Button
type="button"
disabled={!ttsState.hasAudio}
onClick={requestTts}
icon={faBrainCircuit}
>
Generate Another
</Button>
}
<span />
<Button
type="button"
onClick={handleClose}
variant="secondary"
>
{ttsState.hasEnqueued ? 'Close':'Cancel'}
</Button>
</div>
</div>
</TransitionDialogue>
);
};
@@ -1,50 +0,0 @@
import { inferTts } from "~/api";
import {
GenerateTtsAudioRequest,
GenerateTtsAudioSuccess,
GenerateTtsAudioError,
GenerateTtsAudioResponse,
GenerateTtsAudioErrorType,
EndpointResponse
} from './types';
import {
maybeMapError
} from './utilities';
export function GenerateTtsAudioIsError(response: GenerateTtsAudioResponse): response is GenerateTtsAudioError {
return !('inference_job_token' in response);
}
export async function GenerateTtsAudio(request: GenerateTtsAudioRequest) : Promise<GenerateTtsAudioResponse | undefined>
{
return await fetch(inferTts, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
// credentials: 'include',
body: JSON.stringify(request),
})
.then(res => res.json())
.then(res => {
if (!('inference_job_token' in res)) {
return { error: GenerateTtsAudioErrorType.UnknownError };
}
const ret:GenerateTtsAudioSuccess = {
success: true,
inference_job_token: res.inference_job_token,
inference_job_token_type: res.inference_job_token_type,
}
return ret;
})
.catch(e => {
let maybeError = maybeMapError(e);
if (maybeError !== undefined) {
return { error: maybeError };
}
return { error: GenerateTtsAudioErrorType.UnknownError };
});
};
Whitespace-only changes.
@@ -1,83 +0,0 @@
/*
* Types for Listing TTS Models
*/
export interface TtsModelListResponsePayload {
success: boolean,
models: Array<TtsModelListItem>,
}
export interface UserRatings {
positive_count: number,
negative_count: number,
// Total count does not take into account "neutral" ratings.
total_count: number,
}
export interface TtsModelListItem {
model_token: string,
tts_model_type: string,
creator_user_token: string,
creator_username: string,
creator_display_name: string,
creator_gravatar_hash: string,
ietf_language_tag: string,
ietf_primary_language_subtag: string,
updatable_slug: string,
title: string,
is_front_page_featured: boolean,
is_twitch_featured: boolean,
user_ratings: UserRatings,
category_tokens: string[],
created_at: string,
updated_at: string,
}
/*
* Types for Making a TTS Generation Request
*/
export interface GenerateTtsAudioRequest {
uuid_idempotency_token: string,
tts_model_token: string,
inference_text: string,
// TODO(2022-03): TEMPORARY
is_storyteller_demo?: boolean,
}
export interface GenerateTtsAudioSuccess {
success: boolean,
inference_job_token: string,
// Which queue to poll
inference_job_token_type?: string,
}
export interface GenerateTtsAudioError {
error: GenerateTtsAudioErrorType,
}
export enum GenerateTtsAudioErrorType {
BadRequest,
NotFound,
TooManyRequests,
ServerError,
UnknownError,
}
export type GenerateTtsAudioResponse = GenerateTtsAudioSuccess | GenerateTtsAudioError;
export interface EndpointSuccessResponse {
success: boolean,
inference_job_token: string,
inference_job_token_type?: string,
}
export interface EndpointErrorResponse {
success: boolean,
}
export type EndpointResponse = EndpointSuccessResponse | EndpointErrorResponse;
export interface StatusLike {
status: number,
}
@@ -1,41 +0,0 @@
import { listTts } from '~/api';
import {
TtsModelListItem,
TtsModelListResponsePayload,
GenerateTtsAudioErrorType,
StatusLike
} from './types';
export async function ListTtsModels() : Promise<Array<TtsModelListItem>| undefined> {
return await fetch(listTts, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
credentials: 'include',
})
.then(res => res.json())
.then(res => {
const response : TtsModelListResponsePayload = res;
if (!response.success) {
return;
}
return response?.models;
})
.catch(e => {
return undefined;
});
}
export function maybeMapError(statuslike: StatusLike) : GenerateTtsAudioErrorType | undefined {
switch (statuslike.status) {
case 400:
return GenerateTtsAudioErrorType.BadRequest;
case 404:
return GenerateTtsAudioErrorType.NotFound;
case 429:
return GenerateTtsAudioErrorType.TooManyRequests;
case 500:
return GenerateTtsAudioErrorType.ServerError;
}
}
@@ -3,8 +3,9 @@ import { useSignals, useSignalEffect } from "@preact/signals-react/runtime";
import { AuthenticationContext } from "~/contexts/Authentication";
import { MediaItem, AssetType } from "~/pages/PageEnigma/models";
import { TtsModelListItem } from "~/pages/PageEnigma/models/tts";
import { audioItemsFromServer } from "~/pages/PageEnigma/store/mediaFromServer";
import { ListAudioByUser } from "./utilities";
import { ListAudioByUser, ListTtsModels } from "./utilities";
import { inferenceJobs } from "~/pages/PageEnigma/store/inferenceJobs";
import { JobState } from "~/hooks/useInferenceJobManager/useInferenceJobManager";
@@ -12,14 +13,21 @@ import { PageLibrary } from "./pageLibrary";
import { PageTTS } from "./pageTTS";
import { AudioTabPages } from "./types";
export const AudioTabComponent = () => {
export const AudioTab = () => {
// app wide data
useSignals();
const { authState } = useContext(AuthenticationContext);
// local states and data
const [ state, setState ] = useState({
firstLoad: false,
fetchingUserAudio: false,
fetchingVoiceModels: false,
page: AudioTabPages.LIBRARY,
});
const { authState } = useContext(AuthenticationContext);
const [voiceModels, setVoiceModels] = useState<Array<TtsModelListItem>>([]);
const handleListAudioByUser = useCallback((username:string, sessionToken:string)=>{
setState((curr)=>({...curr, fetchingUserAudio:true}));
@@ -44,21 +52,33 @@ export const AudioTabComponent = () => {
});
}, []);
const fetchVoiceModels = useCallback(async (sessionToken:string) => {
ListTtsModels(sessionToken).then(res=>{
if(res) setVoiceModels(res);
});
}, []);
useEffect(()=>{
if (authState.userInfo && authState.sessionToken){
if(state.firstLoad === false && audioItemsFromServer.value.length === 0){
handleListAudioByUser(authState.userInfo.username, authState.sessionToken);
if(state.firstLoad === false){
if( audioItemsFromServer.value.length === 0){
handleListAudioByUser(authState.userInfo.username, authState.sessionToken);
}
if( voiceModels.length === 0){
fetchVoiceModels(authState.sessionToken);
}
setState((curr)=>({...curr, firstLoad:true}));
// completed the first load
}
}
}, [authState, state, handleListAudioByUser]);
}, [authState, state, handleListAudioByUser, voiceModels, fetchVoiceModels]);
useEffect(()=>{
console.info('Audio Tab is mounting')
return()=>{console.info('Audio Tab is dismounting')}
}, []);
useSignalEffect(()=>{
// flagging first load is done
if (state.firstLoad === false && audioItemsFromServer.value.length > 0){
setState(curr=>({...curr, firstLoad: true}));
}
// when inference changes, check if there's a new audio to refresh for
if (inferenceJobs.value.length > 0 && authState.userInfo){
const found = inferenceJobs.value.find((job)=>{
@@ -91,6 +111,7 @@ export const AudioTabComponent = () => {
<PageTTS
changePage={changePage}
sessionToken={authState.sessionToken}
voiceModels={voiceModels}
/>
);
}else{
@@ -1,7 +1,7 @@
import { useCallback, useContext, useEffect } from "react";
import { useSignals } from "@preact/signals-react/runtime";
import { WaveformPlayer } from "./WaveformPlayer";
import { WaveformPlayer } from "~/components";
import { MediaItem } from "~/pages/PageEnigma/models";
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
import {
@@ -0,0 +1 @@
export { AudioTab } from "./AudioTab";
@@ -13,12 +13,13 @@ import {
ListSearchDropdown,
Textarea
} from "~/components";
import { JobState } from "~/hooks/useInferenceJobManager/useInferenceJobManager";
import { TtsModelListItem, GenerateTtsAudioResponse } from "~/pages/PageEnigma/models/tts";
import { addInferenceJob, inferenceJobs} from "../../../store/inferenceJobs";
import { JobState } from "~/hooks/useInferenceJobManager/useInferenceJobManager";
import { ListTtsModels, GenerateTtsAudio, GetMediaFileByToken } from "./utilities";
import { addInferenceJob, inferenceJobs} from "~/pages/PageEnigma/store/inferenceJobs";
import { GenerateTtsAudio, GetMediaFileByToken } from "./utilities";
import { AudioTabPages } from "./types";
import { AudioItemElement } from "./audioItemElement";
import { MediaItem, AssetType } from "~/pages/PageEnigma/models";
@@ -44,9 +45,11 @@ const initialState:TtsState =
export const PageTTS =({
changePage,
sessionToken,
voiceModels
}:{
changePage: (newPage:AudioTabPages) => void;
sessionToken: string;
voiceModels: Array<TtsModelListItem>
})=>{
const [ttsState, setTtsState] = useState<TtsState>(initialState);
@@ -56,8 +59,8 @@ export const PageTTS =({
console.log(inferenceJobs.value);
if(ttsState.hasEnqueued && ttsState.inferenceToken){
const found = inferenceJobs.value.find((job)=>job.job_id===ttsState.inferenceToken);
console.log(`finding: ${ttsState.inferenceToken}`);
console.log(found);
// console.log(`finding: ${ttsState.inferenceToken}`);
// console.log(found);
if(found?.job_status === JobState.COMPLETE_SUCCESS){
setTtsState((curr)=>({
...curr,
@@ -85,23 +88,6 @@ export const PageTTS =({
}
});
const [ttsModels, setTtsModels] = useState<Array<TtsModelListItem>>([]);
const listModels = useCallback(async () => {
const ttsModelsLoaded = ttsModels.length > 0;
if (ttsModelsLoaded) {
return; // Already queried.
}
const models = await ListTtsModels(sessionToken);
if (models) {
setTtsModels(models);
}
}, []);
useEffect(() => {
listModels();
}, [listModels]);
useEffect(()=> {
if(ttsState.hasEnqueued && ttsState.inferenceToken && ttsState.inferenceJobType){
console.log(`tts has Enqueued`);
@@ -158,13 +144,12 @@ export const PageTTS =({
};
const handleOnSelect = (val:string)=>{
console.log( val);
const voiceModel = ttsModels.find((item)=>{
const currVoiceModel = voiceModels.find((item)=>{
if (item.title === val) return item
})
setTtsState((curr)=>({
...curr,
voice: voiceModel,
voice: currVoiceModel,
}));
}
@@ -178,8 +163,8 @@ export const PageTTS =({
<H2>Generate TTS</H2>
</div>
<Label className="mb-1">Select a Voice</Label>
{ttsModels.length > 0 && <ListSearchDropdown
list={ttsModels}
{voiceModels.length > 0 && <ListSearchDropdown
list={voiceModels}
listDisplayKey="title"
onSelect={handleOnSelect}
/> }
@@ -1 +0,0 @@
export { AudioTabComponent } from "./AudioTabComponent";
@@ -1 +1 @@
export { AudioTabComponent as AudioTab } from "../AudioTabComponent";
export { AudioTab} from "../AudioTab";