Merge pull request #363 from storytold/olivicmic-fy-voice-designer-consume-api-react

added config object to useVoiceRequests to activate dataset/voice list fetches
This commit is contained in:
Victor O authored and GitHub committed 2023-11-16 19:56:21 -08:00
commit 9563aec562
9 files changed
+113 -17

No files matched your search

@@ -0,0 +1,89 @@
import React, { useEffect, useState } from 'react';
// import { a } from '@react-spring/web';
import {
GetPendingTtsJobCount,
GetPendingTtsJobCountIsOk,
GetPendingTtsJobCountSuccessResponse,
} from "@storyteller/components/src/api/tts/GetPendingTtsJobCount";
// import { springs } from "resources";
import { useInferenceJobs } from "hooks";
import { Button } from 'components/common';
import { Analytics } from "common/Analytics";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faChevronRight, faHourglass1, faRemove, faTrophy, faWarning } from "@fortawesome/free-solid-svg-icons";
const DEFAULT_QUEUE_REFRESH_INTERVAL_MILLIS = 15000;
export default function InferenceJobsList({ t }: { t: any }) {
const { inferenceJobs = [] } = useInferenceJobs({ type: 0 });
const [pending, pendingSet] = useState<GetPendingTtsJobCountSuccessResponse>({
success: true,
pending_job_count: 0,
cache_time: new Date(0), // NB: Epoch is used for vector clock's initial state
refresh_interval_millis: DEFAULT_QUEUE_REFRESH_INTERVAL_MILLIS,
});
const statusIcons = [faHourglass1,faHourglass1,faWarning,faRemove,faTrophy];
const statusTxt = (which: number, config = {}) => ["animationPending","animationInProgress","animationFailed","animationDead","animationSuccess"].map((str,i) => t(`status.${str}`,config))[which];
const processFail = (fail = "") => {
switch (fail) {
case "face_not_detected": return "Face not detected, try another picture";
default: return "Uknown failure";
}
};
// const transitions = useTransition(inferenceJobs, { // not today
// ...springs.soft,
// from: { opacity: 0, position: "absolute" },
// enter: { opacity: 1, position: "relative" },
// leave: { opacity: 0, position: "absolute" },
// });
useEffect(() => {
const fetch = async () => {
const response = await GetPendingTtsJobCount();
if (GetPendingTtsJobCountIsOk(response)) {
if (
response.cache_time.getTime() > pending.cache_time.getTime()
) {
pendingSet(response);
}
}
};
// TODO: We're having an outage and need to lower this.
//const interval = setInterval(async () => fetch(), 15000);
const refreshInterval = Math.max(
DEFAULT_QUEUE_REFRESH_INTERVAL_MILLIS,
pending.refresh_interval_millis
);
const interval = setInterval(async () => fetch(), refreshInterval);
fetch();
return () => clearInterval(interval);
}, [pending]);
return inferenceJobs.length ? <div {...{ className: "face-animator-jobs panel" }}>
<h5>{ t("headings.yourJobs") }</h5>
{ inferenceJobs.map((job, key) => {
return <div {...{ className: "panel face-animator-job", key }}>
<FontAwesomeIcon {...{ className: `job-status-icon job-status-${job.statusIndex}`, icon: statusIcons[job.statusIndex] }}/>
<div {...{ className: "job-details" }}>
<h4>
{ statusTxt(job.statusIndex,{ attemptCount: job.attemptCount || "" }) }
</h4>
<span>
{ job.maybeFailureCategory ? `${ processFail(job.maybeFailureCategory) }` : "" }
</span>
</div>
{
job.maybeResultToken ? <Button {...{
href: `media/${job.maybeResultToken}`,
icon: faChevronRight,
iconFlip: true,
label: t("inputs.viewResult"),
onClick:() => Analytics.voiceConversionClickDownload()
}} />: null
}
</div>
}).reverse()}
</div> : null;
};
@@ -0,0 +1 @@
export { default } from './InferenceJobsList';
@@ -22,7 +22,7 @@ import FaceAnimatorTitle from "./FaceAnimatorTitle";
import FaceAnimatorInput from "./FaceAnimatorInput";
import FaceAnimatorWorking from "./FaceAnimatorWorking";
import FaceAnimatorSuccess from "./FaceAnimatorSuccess";
import FaceAnimatorJobs from "./FaceAnimatorJobs";
import InferenceJobsList from "components/layout/InferenceJobsList";
import { FaceAnimatorCore } from "./FaceAnimatorTypes";
import { BasicVideo } from "components/common";
import "./LipsyncEditor.scss";
@@ -182,7 +182,7 @@ export default function LipsyncEditor({
);
})}
</div>
<FaceAnimatorJobs {...{ inferenceJobsByCategory, t }}/>
<InferenceJobsList {...{ t }}/>
<div {...{ className: "face-animator-mobile-sample" }}>
<BasicVideo {...{ src: "/videos/face-animator-instruction-en.mp4" }} />
</div>
@@ -19,7 +19,7 @@ interface RouteParams {
}
export default function DatasetEditor({ value }: Props) {
const { datasets, inputCtrl } = useVoiceRequests();
const { datasets, inputCtrl } = useVoiceRequests({});
const [title, titleSet] = useState("");
const [language,languageSet] = useState("en");
const [visibility,visibilitySet] = useState("");
@@ -28,7 +28,7 @@ interface RouteParams {
function VoiceDesignerFormPage() {
const history = useHistory();
const { datasets, inputCtrl, languages, visibilityOptions, voices } = useVoiceRequests();
const { datasets, inputCtrl, languages, visibilityOptions, voices } = useVoiceRequests({});
const [language, languageSet] = useState("en");
const [visibility, visibilitySet] = useState("");
const [title, titleSet] = useState("");
@@ -1,6 +1,8 @@
import React, { useState } from "react";
import { useHistory, useLocation } from "react-router-dom";
import { faPlus, faWaveform } from "@fortawesome/pro-solid-svg-icons";
import InferenceJobsList from "components/layout/InferenceJobsList";
import { useLocalize } from "hooks";
// import { usePrefixedDocumentTitle } from "common/UsePrefixedDocumentTitle";
import Panel from "components/common/Panel";
import PageHeader from "components/layout/PageHeader";
@@ -12,7 +14,8 @@ import useVoiceRequests from "./useVoiceRequests";
function VoiceDesignerMainPage() {
const { pathname } = useLocation();
const { datasets, voices } = useVoiceRequests();
const { t } = useLocalize("FaceAnimator");
const { datasets, voices } = useVoiceRequests({ requestDatasets: true, requestVoices: true });
const [isDeleteVoiceModalOpen, setIsDeleteVoiceModalOpen] = useState(false);
const [isDeleteDatasetModalOpen, setIsDeleteDatasetModalOpen] =
useState(false);
@@ -128,7 +131,7 @@ function VoiceDesignerMainPage() {
panel={false}
imageUrl="/images/header/voice-designer.png"
/>
<InferenceJobsList {...{ t }}/>
<Panel mb={true}>
<nav>
<ul className="nav nav-tabs">
@@ -60,7 +60,7 @@ export default function VoiceDesignerUseVoicePage(
voiceToken: "",
languageTag: "",
});
const { inference } = useVoiceRequests();
const { inference } = useVoiceRequests({});
const [isEnqueuing, setIsEnqueuing] = useState(false);
const getVoiceDetails = useCallback(async (voice_token) => {
@@ -24,7 +24,7 @@ function VoiceDesignerVoiceEditPage() {
const history = useHistory();
const { voice_token } = useParams();
const { inputCtrl, languages, visibilityOptions, voices } = useVoiceRequests();
const { inputCtrl, languages, visibilityOptions, voices } = useVoiceRequests({});
usePrefixedDocumentTitle("Edit Voice");
@@ -24,18 +24,18 @@ import { UpdateDataset, UpdateDatasetRequest, UpdateDatasetResponse } from "@sto
import { EnqueueTts } from "@storyteller/components/src/api/voice_designer/inference/EnqueueTts";
import { useSession } from "hooks";
export default function useVoiceRequests() {
export default function useVoiceRequests({ requestDatasets = false, requestVoices = false }){
// this state will be provided as params, triggering the appropriate api call if present
const [datasets, datasetsSet] = useState<Dataset[]>([]);
const [voices, voicesSet] = useState<Voice[]>([]);
// this state stays here, fetched states are not success, merely an attempt was made. Each list fetch sets to true. Set to false to retry
const [fetchedDatasets, fetchedDatasetsSet] = useState(false);
const [fetchedVoices,fetchedVoicesSet] = useState(false);
const [fetchDatasets, fetchDatasetsSet] = useState(requestDatasets);
const [fetchVoices,fetchVoicesSet] = useState(requestVoices);
const { user } = useSession();
// const [timestamp, timestampSet] = useState(Date.now());
const refreshData = () => { fetchedDatasetsSet(false); fetchedVoicesSet(false); }; // later we can do refresh per list
const refreshData = () => { fetchDatasetsSet(true); fetchVoicesSet(true); }; // later we can do refresh per list
const createDataset = (urlRouteArgs: string, request: CreateDatasetRequest): Promise<CreateDatasetResponse> =>
CreateDataset(urlRouteArgs, request).then(res => {
@@ -80,6 +80,8 @@ export default function useVoiceRequests() {
});
};
const listDatasets = () => { fetchDatasetsSet(true); return datasets; }
const languages = [
{ value: "en", label: "English" },
{ value: "es", label: "Spanish" },
@@ -93,20 +95,20 @@ export default function useVoiceRequests() {
useEffect(() => {
if (user && user.username) {
if (!fetchedDatasets) {
fetchedDatasetsSet(true);
if (fetchDatasets) {
fetchDatasetsSet(false);
ListDatasetsByUser(user.username,{}).then(res => {
if (res.datasets) datasetsSet(res.datasets);
});
}
if (!fetchedVoices) {
fetchedVoicesSet(true);
if (fetchVoices) {
fetchVoicesSet(false);
ListVoicesByUser(user.username,{}).then(res => {
if (res.voices) voicesSet(res.voices);
});
}
}
}, [user, fetchedDatasets, fetchedVoices]);
}, [user, fetchDatasets, fetchVoices]);
return {
datasets: {
@@ -116,6 +118,7 @@ export default function useVoiceRequests() {
edit: editDataSet,
get: GetDataset,
list: datasets,
listDatasets,
refresh: refreshData
},
inference: {