mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 01:38:44 +00:00
5 files changed
+251
-1
No files matched your search
@@ -3,6 +3,7 @@ import { JobState, jobStateFromString } from "./JobStates";
|
||||
|
||||
// Type of inference job (specified by the frontend, not backend)
|
||||
export enum FrontendInferenceJobType {
|
||||
FaceAnimation,
|
||||
TextToSpeech,
|
||||
VoiceConversion,
|
||||
}
|
||||
|
||||
@@ -180,6 +180,7 @@ function newVocodes() {
|
||||
|
||||
function initInferenceJobsByCategoryMap() : Map<FrontendInferenceJobType, InferenceJob[]> {
|
||||
let inferenceJobsByCategory = new Map();
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.FaceAnimation, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.TextToSpeech, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VoiceConversion, []);
|
||||
return inferenceJobsByCategory;
|
||||
|
||||
@@ -533,6 +533,9 @@ class PageContainer extends React.Component<Props, State> {
|
||||
|
||||
<Route path="/testing">
|
||||
<TestingPage
|
||||
sessionSubscriptionsWrapper={
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}
|
||||
enqueueInferenceJob={this.props.enqueueInferenceJob}
|
||||
inferenceJobs={this.props.inferenceJobs}
|
||||
inferenceJobsByCategory={this.props.inferenceJobsByCategory}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { t } from "i18next";
|
||||
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
|
||||
import { JobState } from "@storyteller/components/src/jobs/JobStates";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faHeadphonesSimple,
|
||||
faDownload,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
|
||||
import { Analytics } from "../../../../common/Analytics";
|
||||
import {
|
||||
GetPendingTtsJobCount,
|
||||
GetPendingTtsJobCountIsOk,
|
||||
GetPendingTtsJobCountSuccessResponse,
|
||||
} from "@storyteller/components/src/api/tts/GetPendingTtsJobCount";
|
||||
import { InferenceJob } from "@storyteller/components/src/jobs/InferenceJob";
|
||||
|
||||
interface Props {
|
||||
inferenceJobs: Array<InferenceJob>;
|
||||
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
|
||||
}
|
||||
|
||||
// TODO: This is duplicated in SessionTtsInferenceResultsList !
|
||||
// Default to querying every 15 seconds, but make it configurable serverside
|
||||
const DEFAULT_QUEUE_REFRESH_INTERVAL_MILLIS = 15000;
|
||||
|
||||
function TestPageResultList(props: Props) {
|
||||
const [pendingTtsJobs, setPendingTtsJobs] =
|
||||
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,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const fetch = async () => {
|
||||
const response = await GetPendingTtsJobCount();
|
||||
if (GetPendingTtsJobCountIsOk(response)) {
|
||||
if (
|
||||
response.cache_time.getTime() > pendingTtsJobs.cache_time.getTime()
|
||||
) {
|
||||
setPendingTtsJobs(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,
|
||||
pendingTtsJobs.refresh_interval_millis
|
||||
);
|
||||
const interval = setInterval(async () => fetch(), refreshInterval);
|
||||
fetch();
|
||||
return () => clearInterval(interval);
|
||||
}, [pendingTtsJobs]);
|
||||
|
||||
let results: Array<JSX.Element> = [];
|
||||
|
||||
// TODO(bt,2023-04-08): Clean this utter garbage duplication up.
|
||||
|
||||
// ============================= GENERIC INFERENCE =============================
|
||||
|
||||
props.inferenceJobs.forEach((job) => {
|
||||
if (!job.maybeResultToken) {
|
||||
let cssStyle = "alert alert-secondary mb-0";
|
||||
let stateDescription = "Pending...";
|
||||
|
||||
switch (job.jobState) {
|
||||
case JobState.PENDING:
|
||||
case JobState.UNKNOWN:
|
||||
stateDescription =
|
||||
job.maybeExtraStatusDescription == null
|
||||
? t("common.SessionTtsInferenceResults.progress.pending")
|
||||
: job.maybeExtraStatusDescription;
|
||||
break;
|
||||
case JobState.STARTED:
|
||||
cssStyle = "alert alert-success mb-0";
|
||||
stateDescription =
|
||||
job.maybeExtraStatusDescription == null
|
||||
? t("common.SessionTtsInferenceResults.progress.started")
|
||||
: job.maybeExtraStatusDescription;
|
||||
break;
|
||||
case JobState.ATTEMPT_FAILED:
|
||||
cssStyle = "alert alert-danger mb-0";
|
||||
stateDescription = `Failed ${job.attemptCount} attempt(s). Will retry...`;
|
||||
break;
|
||||
case JobState.COMPLETE_FAILURE:
|
||||
case JobState.DEAD:
|
||||
cssStyle = "alert alert-danger mb-0";
|
||||
// TODO(bt,2023-01-23): Translate when I can test it
|
||||
stateDescription = t(
|
||||
"common.SessionTtsInferenceResults.progress.dead"
|
||||
);
|
||||
break;
|
||||
case JobState.COMPLETE_SUCCESS:
|
||||
cssStyle = "message is-success mb-0";
|
||||
// Not sure why we're here instead of other branch!
|
||||
stateDescription = t(
|
||||
"common.SessionTtsInferenceResults.progress.success"
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
results.push(
|
||||
<div key={job.jobToken}>
|
||||
<div>
|
||||
<div>
|
||||
<div className={cssStyle}>{stateDescription}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
let audioLink = new BucketConfig().getGcsUrl(
|
||||
job.maybeResultPublicBucketMediaPath
|
||||
);
|
||||
//let ttsPermalink = `/tts/result/${job.maybeResultToken}`;
|
||||
|
||||
//let wavesurfers = <SessionTtsAudioPlayer filename={audioLink} />;
|
||||
let wavesurfers : string[] = [];
|
||||
|
||||
let audioDownloadFilename = `fakeyou-${job.jobToken}.wav`;
|
||||
|
||||
results.push(
|
||||
<div key={job.jobToken}>
|
||||
{/*<div className="message-header">
|
||||
<p>{job.title}</p>
|
||||
<button className="delete" aria-label="delete"></button>
|
||||
</div>*/}
|
||||
<div>
|
||||
<div className="panel panel-tts-results p-4 gap-3 d-flex flex-column">
|
||||
<div>
|
||||
<h5 className="mb-2">{job.maybeModelTitle}</h5>
|
||||
<p>{job.maybeRawInferenceText}</p>
|
||||
</div>
|
||||
|
||||
{/* <audio
|
||||
className="w-100"
|
||||
controls
|
||||
src={audioLink}
|
||||
onClick={() => {
|
||||
Analytics.ttsClickResultInlinePlay();
|
||||
}}
|
||||
>
|
||||
Your browser does not support the
|
||||
<code>audio</code> element.
|
||||
</audio> */}
|
||||
|
||||
{wavesurfers}
|
||||
|
||||
<div className="mt-2">
|
||||
{/*<Link
|
||||
to={ttsPermalink}
|
||||
onClick={() => {
|
||||
Analytics.ttsClickResultLink();
|
||||
}}
|
||||
className="fw-semibold"
|
||||
>
|
||||
<FontAwesomeIcon icon={faLink} className="me-2" />
|
||||
{t("common.SessionTtsInferenceResults.result.shareDownload")}
|
||||
</Link>*/}
|
||||
|
||||
<a
|
||||
className=" btn btn-primary w-100 mt-4"
|
||||
href={audioLink}
|
||||
onClick={() => {
|
||||
Analytics.voiceConversionClickDownload();
|
||||
}}
|
||||
download={audioDownloadFilename}
|
||||
>
|
||||
<FontAwesomeIcon icon={faDownload} className="me-2" />
|
||||
Download File{" "}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let noResultsSection = (
|
||||
<div className="panel panel-inner text-center p-5 rounded-5 h-100">
|
||||
<div className="d-flex flex-column opacity-75 h-100 justify-content-center">
|
||||
<FontAwesomeIcon icon={faHeadphonesSimple} className="fs-3 mb-3" />
|
||||
<h5 className="fw-semibold">
|
||||
{t("common.SessionTtsInferenceResults.noResults.title")}
|
||||
</h5>
|
||||
<p>{t("common.SessionTtsInferenceResults.noResults.subtitle")}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (results.length === 0) {
|
||||
return <>{noResultsSection}</>;
|
||||
}
|
||||
|
||||
// Users have requested reverse chronological results
|
||||
results.reverse();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<div className="d-flex flex-column gap-3">
|
||||
<div className="d-flex flex-column gap-3">{results}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { TestPageResultList };
|
||||
@@ -10,8 +10,13 @@ import {
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { FrontendInferenceJobType, InferenceJob } from "@storyteller/components/src/jobs/InferenceJob";
|
||||
import TestUploadComponent from "./TestUploadComponent";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faBarsStaggered } from "@fortawesome/free-solid-svg-icons";
|
||||
import { TestPageResultList } from "./TestPageResultList";
|
||||
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
|
||||
|
||||
interface Props {
|
||||
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
|
||||
enqueueInferenceJob: (
|
||||
jobToken: string,
|
||||
frontendInferenceJobType: FrontendInferenceJobType
|
||||
@@ -46,7 +51,7 @@ function TestingPage(props: Props) {
|
||||
if (EnqueueFaceAnimationIsSuccess(result)) {
|
||||
props.enqueueInferenceJob(
|
||||
result.inference_job_token,
|
||||
FrontendInferenceJobType.VoiceConversion
|
||||
FrontendInferenceJobType.FaceAnimation
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -113,6 +118,31 @@ function TestingPage(props: Props) {
|
||||
onClick={() => doTest()}>
|
||||
Test the thing
|
||||
</button>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<h4 className="text-center text-lg-start">
|
||||
<FontAwesomeIcon
|
||||
icon={faBarsStaggered}
|
||||
className="me-3"
|
||||
/>
|
||||
Session Results
|
||||
</h4>
|
||||
<div className="d-flex flex-column gap-3 session-tts-section session-vc-section">
|
||||
<TestPageResultList
|
||||
inferenceJobs={
|
||||
props.inferenceJobsByCategory.get(
|
||||
FrontendInferenceJobType.FaceAnimation
|
||||
)!
|
||||
}
|
||||
sessionSubscriptionsWrapper={
|
||||
props.sessionSubscriptionsWrapper
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user