mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 01:38:44 +00:00
Merge branch 'master' into olivicmic-fy-voice-designer-consume-api-react
This commit is contained in:
commit
fa5e716ca8
14 files changed
+461
-66
No files matched your search
@@ -22,8 +22,6 @@ export default function GetApiHost() {
|
||||
return hostConfig("api.fakeyou.com");
|
||||
case "devproxy.storyteller.ai":
|
||||
return hostConfig("api.storyteller.ai");
|
||||
case "dev.fakeyou.com":
|
||||
return hostConfig("api.dev.fakeyou.com"); // NB: for dev machines with nginx proxies
|
||||
case "dev.fakeyou.com":
|
||||
return hostConfig("api.dev.fakeyou.com:12345",false); // false disables SSL
|
||||
default:
|
||||
|
||||
@@ -10,19 +10,16 @@ export class FakeYouFrontendEnvironment {
|
||||
environment: Environment;
|
||||
|
||||
private constructor() {
|
||||
switch (document.location.host) {
|
||||
const domainWithoutPort = document.location.host.split(":")[0];
|
||||
|
||||
switch (domainWithoutPort) {
|
||||
case "fakeyou.com":
|
||||
this.environment = Environment.Production;
|
||||
break;
|
||||
case "staging.fakeyou.com":
|
||||
this.environment = Environment.Staging;
|
||||
break;
|
||||
case "localhost:7000":
|
||||
this.environment = Environment.Development;
|
||||
break;
|
||||
case "localhost:7001":
|
||||
this.environment = Environment.Development;
|
||||
break;
|
||||
case "localhost":
|
||||
case "dev.fakeyou.com":
|
||||
this.environment = Environment.Development;
|
||||
break;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
faSignOutAlt,
|
||||
faFaceViewfinder,
|
||||
faCloudUpload,
|
||||
faWandMagicSparkles,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import Button from "components/common/Button/Button";
|
||||
@@ -39,7 +40,7 @@ export default function SideNav(props: SideNavProps) {
|
||||
let history = useHistory();
|
||||
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
|
||||
const fakeYouFrontendEnv = FakeYouFrontendEnvironment.getInstance();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const isDevelopmentEnv = fakeYouFrontendEnv.isDevelopment()
|
||||
const handleNavLinkClick = () => {
|
||||
const wrapper = document.getElementById("wrapper");
|
||||
@@ -270,7 +271,6 @@ export default function SideNav(props: SideNavProps) {
|
||||
{t("speechVc")}
|
||||
</NavLink>
|
||||
</li>
|
||||
{isDevelopmentEnv && (
|
||||
<li>
|
||||
<NavLink
|
||||
to="/voice-designer"
|
||||
@@ -278,13 +278,12 @@ export default function SideNav(props: SideNavProps) {
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faWaveformLines}
|
||||
icon={faWandMagicSparkles}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
{"Voice Designer"}
|
||||
</NavLink>
|
||||
</li>
|
||||
)}
|
||||
<hr className="mb-4 mt-3" />
|
||||
<li className="sidebar-heading">{t("videoTitle")}</li>
|
||||
<li>
|
||||
|
||||
@@ -10,6 +10,7 @@ const useUploadedFiles = create<UploadedFilesState>((set) => ({
|
||||
setFiles: (files: any[]) => set({ files }),
|
||||
audioLinks: [],
|
||||
setAudioLinks: (audioLinks: string[]) => set({ audioLinks }),
|
||||
deleteEverything:() => set({ files: [], audioLinks: [] }),
|
||||
}));
|
||||
|
||||
export default useUploadedFiles;
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { TtsInferenceJob } from "../../../App";
|
||||
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
|
||||
import { JobState } from "@storyteller/components/src/jobs/JobStates";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faClock,
|
||||
faLink,
|
||||
faHeadphonesSimple,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
|
||||
import { Analytics } from "../../../common/Analytics";
|
||||
import { SessionTtsAudioPlayer } from "./SessionTtsAudioPlayer";
|
||||
import { WebUrl } from "../../../common/WebUrl";
|
||||
import {
|
||||
GetPendingTtsJobCount,
|
||||
GetPendingTtsJobCountIsOk,
|
||||
GetPendingTtsJobCountSuccessResponse,
|
||||
} from "@storyteller/components/src/api/tts/GetPendingTtsJobCount";
|
||||
import { InferenceJob } from "@storyteller/components/src/jobs/InferenceJob";
|
||||
import { useLocalize } from "hooks";
|
||||
|
||||
interface Props {
|
||||
inferenceJobs: Array<InferenceJob>;
|
||||
ttsInferenceJobs: Array<TtsInferenceJob>;
|
||||
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 SessionVoiceDesignerInferenceResultsList(props: Props) {
|
||||
const { t } = useLocalize("SessionTtsInferenceResultList");
|
||||
|
||||
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("resultsProgressPending")
|
||||
: job.maybeExtraStatusDescription;
|
||||
break;
|
||||
case JobState.STARTED:
|
||||
cssStyle = "alert alert-success mb-0";
|
||||
stateDescription =
|
||||
job.maybeExtraStatusDescription == null
|
||||
? t("resultsProgressStarted")
|
||||
: job.maybeExtraStatusDescription;
|
||||
break;
|
||||
case JobState.ATTEMPT_FAILED:
|
||||
cssStyle = "alert alert-danger mb-0";
|
||||
stateDescription = `${t("resultsProgressFail", {
|
||||
0: job.attemptCount || "0",
|
||||
})}}`;
|
||||
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("resultsProgressDead");
|
||||
break;
|
||||
case JobState.COMPLETE_SUCCESS:
|
||||
cssStyle = "message is-success mb-0";
|
||||
// Not sure why we're here instead of other branch!
|
||||
stateDescription = t("resultsProgressSuccess");
|
||||
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 = `/media/${job.maybeResultToken}`;
|
||||
|
||||
let wavesurfers = <SessionTtsAudioPlayer filename={audioLink} />;
|
||||
|
||||
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("resultsAudioShareDownload")}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO(bt,2023-04-08): Clean this utter garbage duplication up.
|
||||
|
||||
// ============================= LEGACY TTS =============================
|
||||
|
||||
props.ttsInferenceJobs.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("resultsProgressPending")
|
||||
: job.maybeExtraStatusDescription;
|
||||
break;
|
||||
case JobState.STARTED:
|
||||
cssStyle = "alert alert-success mb-0";
|
||||
stateDescription =
|
||||
job.maybeExtraStatusDescription == null
|
||||
? t("resultsProgressStarted")
|
||||
: job.maybeExtraStatusDescription;
|
||||
break;
|
||||
case JobState.ATTEMPT_FAILED:
|
||||
cssStyle = "alert alert-danger mb-0";
|
||||
stateDescription = `${t("resultsProgressFail", {
|
||||
0: job.attemptCount || "0",
|
||||
})}}`;
|
||||
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("resultsProgressDead");
|
||||
break;
|
||||
case JobState.COMPLETE_SUCCESS:
|
||||
cssStyle = "message is-success mb-0";
|
||||
// Not sure why we're here instead of other branch!
|
||||
stateDescription = t("resultsProgressSuccess");
|
||||
break;
|
||||
}
|
||||
|
||||
results.push(
|
||||
<div key={job.jobToken}>
|
||||
<div>
|
||||
<div>
|
||||
<div className={cssStyle}>{stateDescription}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
let audioLink = new BucketConfig().getGcsUrl(
|
||||
job.maybePublicBucketWavAudioPath
|
||||
);
|
||||
let ttsPermalink = `/tts/result/${job.maybeResultToken}`;
|
||||
|
||||
let wavesurfers = <SessionTtsAudioPlayer filename={audioLink} />;
|
||||
|
||||
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.title}</h5>
|
||||
<p>{job.rawInferenceText}</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("resultsAudioShareDownload")}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// ============================= END LEGACY TTS =============================
|
||||
|
||||
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("resultsBlankText")}</h5>
|
||||
<p>{t("resultsBlankSubText")}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (results.length === 0) {
|
||||
return <>{noResultsSection}</>;
|
||||
}
|
||||
|
||||
let upgradeNotice = <></>;
|
||||
|
||||
// Ask non-premium users to upgrade
|
||||
if (
|
||||
results.length !== 0 &&
|
||||
!props.sessionSubscriptionsWrapper.hasPaidFeatures()
|
||||
) {
|
||||
upgradeNotice = (
|
||||
<div className="d-flex flex-column gap-3 sticky-top zi-2">
|
||||
<div className="alert alert-warning alert-cta mb-0">
|
||||
<FontAwesomeIcon icon={faClock} className="me-2" />
|
||||
{t("resultsUpgradeNotice")}{" "}
|
||||
<Link
|
||||
to={WebUrl.pricingPageWithReferer("nowait")}
|
||||
onClick={() => {
|
||||
Analytics.ttsTooSlowUpgradePremium();
|
||||
}}
|
||||
className="alert-link fw-semibold"
|
||||
>
|
||||
{t("resultsUpgradeLinkText")}
|
||||
</Link>{" "}
|
||||
({t("resultsTtsQueued")}{" "}
|
||||
<span className="text-red">~{pendingTtsJobs.pending_job_count}</span>)
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Users have requested reverse chronological results
|
||||
results.reverse();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<div className="d-flex flex-column gap-3">
|
||||
{upgradeNotice}
|
||||
<div className="d-flex flex-column gap-3">{results}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { SessionVoiceDesignerInferenceResultsList };
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
faDiscord,
|
||||
faTwitter,
|
||||
faTiktok,
|
||||
|
||||
} from "@fortawesome/free-brands-svg-icons";
|
||||
import { ThirdPartyLinks } from "@storyteller/components/src/constants/ThirdPartyLinks";
|
||||
import {
|
||||
@@ -213,6 +214,10 @@ function FooterNav(props: Props) {
|
||||
<Link to="/voice-conversion">{t("productVc")}</Link>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<Link to="/voice-designer">Voice Designer</Link>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<Link to="/face-animator">{t("productFaceAnimator")}</Link>
|
||||
</li>
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
faSignOutAlt,
|
||||
faBook,
|
||||
faStar,
|
||||
faWandMagicSparkles
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faDiscord } from "@fortawesome/free-brands-svg-icons";
|
||||
import { Logout } from "@storyteller/components/src/api/session/Logout";
|
||||
@@ -244,6 +245,15 @@ function TopNav(props: Props) {
|
||||
{t("productVc")}
|
||||
</Link>
|
||||
</li>
|
||||
<li data-bs-toggle="offcanvas">
|
||||
<Link className="dropdown-item" to="/voice-designer">
|
||||
<FontAwesomeIcon
|
||||
icon={faWandMagicSparkles}
|
||||
className="me-2"
|
||||
/>
|
||||
Voice Designer
|
||||
</Link>
|
||||
</li>
|
||||
<li data-bs-toggle="offcanvas">
|
||||
<Link className="dropdown-item" to="/face-animator">
|
||||
<FontAwesomeIcon
|
||||
|
||||
@@ -298,6 +298,40 @@ function LandingPage(props: Props) {
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="col-12 col-md-4">
|
||||
<Link
|
||||
to="/voice-designer"
|
||||
className="panel panel-select d-flex flex-column align-items-center"
|
||||
>
|
||||
<div className="d-flex px-4 pt-4 align-items-start w-100">
|
||||
<div className="flex-grow-1">
|
||||
<div className="mb-1">
|
||||
<span className="badge-new d-inline-flex align-items-center mb-2 me-2">
|
||||
<FontAwesomeIcon icon={faSparkles} className="me-1" />
|
||||
{t("productNewTag")}
|
||||
</span>
|
||||
|
||||
<h4 className="fw-bold text-white d-inline-flex align-items-center mb-0">
|
||||
<span>Voice Designer</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<h6 className="fw-normal opacity-75 text-white">
|
||||
Make yourself sound like anyone
|
||||
</h6>
|
||||
</div>
|
||||
<Link to="/voice-designer" className="btn btn-square mt-1">
|
||||
<FontAwesomeIcon icon={faArrowRight} />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<img
|
||||
className="img-fluid"
|
||||
src="/images/landing/hanashi-after.webp"
|
||||
alt="Voice Designer"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="col-12 col-md-4">
|
||||
<Link
|
||||
to="/face-animator"
|
||||
|
||||
@@ -21,6 +21,7 @@ import { v4 as uuidv4 } from "uuid";
|
||||
import { useFile } from "hooks";
|
||||
|
||||
import useVoiceRequests from "./useVoiceRequests";
|
||||
import useUploadedFiles from "hooks/useUploadedFiles";
|
||||
|
||||
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
|
||||
|
||||
@@ -35,7 +36,7 @@ function VoiceDesignerFormPage({ enqueueInferenceJob }: { enqueueInferenceJob: a
|
||||
const [visibility, visibilitySet] = useState("hidden");
|
||||
const [title, titleSet] = useState("");
|
||||
const [fetched,fetchedSet] = useState(false);
|
||||
|
||||
const deleteEverything = useUploadedFiles((state: any) => state.deleteEverything);
|
||||
const audioProps = useFile({}); // contains upload inout state and controls, see docs
|
||||
|
||||
const datasetInputs = [
|
||||
@@ -72,6 +73,7 @@ function VoiceDesignerFormPage({ enqueueInferenceJob }: { enqueueInferenceJob: a
|
||||
|
||||
const initialStep = history.location.pathname.includes("/upload") ? 1 : 0;
|
||||
const [currentStep, setCurrentStep] = useState(initialStep);
|
||||
const [audioSamplesReady, setAudioSamplesReady] = useState(false);
|
||||
|
||||
const steps = isEditMode
|
||||
? ["Edit Details", "Edit Samples"]
|
||||
@@ -83,7 +85,7 @@ function VoiceDesignerFormPage({ enqueueInferenceJob }: { enqueueInferenceJob: a
|
||||
return <VoiceDetails {...{ datasetInputs }} />;
|
||||
case 1:
|
||||
return (
|
||||
<UploadSamples {...{ audioProps, datasetToken: dataset_token }} />
|
||||
<UploadSamples key={!isNewCreation && dataset_token || uuidv4()} {...{ audioProps, datasetToken: dataset_token, setAudioSamplesReady }} />
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
@@ -111,6 +113,7 @@ function VoiceDesignerFormPage({ enqueueInferenceJob }: { enqueueInferenceJob: a
|
||||
idempotency_token: uuidv4(),
|
||||
}).then((res: any) => {
|
||||
if (res && res.success && res.token) {
|
||||
deleteEverything();
|
||||
history.push(`/voice-designer/dataset/${ res.token }/upload`);
|
||||
}
|
||||
});
|
||||
@@ -140,6 +143,7 @@ function VoiceDesignerFormPage({ enqueueInferenceJob }: { enqueueInferenceJob: a
|
||||
voice_dataset_token: dataset_token || "",
|
||||
})
|
||||
.then((res: any) => {
|
||||
deleteEverything();
|
||||
if (res && res.success) {
|
||||
enqueueInferenceJob(
|
||||
res.inference_job_token,
|
||||
@@ -194,6 +198,7 @@ function VoiceDesignerFormPage({ enqueueInferenceJob }: { enqueueInferenceJob: a
|
||||
onBack={handleBack}
|
||||
onNext={handleNext}
|
||||
onCreate={handleCreateVoice}
|
||||
createDisabled={!audioSamplesReady}
|
||||
/>
|
||||
</Panel>
|
||||
</Container>
|
||||
|
||||
@@ -20,28 +20,43 @@ import {
|
||||
function VoiceDesignerMainPage({ inferenceJobsByCategory }: { inferenceJobsByCategory: any }) {
|
||||
const { pathname } = useLocation();
|
||||
const { t } = useLocalize("FaceAnimator");
|
||||
const { datasets, voices } = useVoiceRequests({ requestDatasets: true, requestVoices: true });
|
||||
const [isDeleteVoiceModalOpen, setIsDeleteVoiceModalOpen] = useState(false);
|
||||
const [isDeleteDatasetModalOpen, setIsDeleteDatasetModalOpen] =
|
||||
useState(false);
|
||||
const { datasets, voices } = useVoiceRequests({
|
||||
requestDatasets: true,
|
||||
requestVoices: true,
|
||||
});
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const view = ["/voice-designer/datasets", "/voice-designer/voices"].indexOf(
|
||||
pathname
|
||||
);
|
||||
const [deleteItem, setDeleteItem] = useState("");
|
||||
const [deleteType, setDeleteType] = useState("");
|
||||
const [deleteText, setDeleteText] = useState({
|
||||
title: "",
|
||||
text: "",
|
||||
});
|
||||
|
||||
// const openDeleteVoiceModal = () => {
|
||||
// setIsDeleteVoiceModalOpen(true);
|
||||
// };
|
||||
|
||||
const closeDeleteVoiceModal = () => {
|
||||
setIsDeleteVoiceModalOpen(false);
|
||||
const openDeleteModal = (token: string, type: string) => {
|
||||
setDeleteItem(token);
|
||||
setDeleteType(type);
|
||||
setDeleteText({
|
||||
title: `Delete ${type}`,
|
||||
text: `Are you sure you want to delete this ${type}?`,
|
||||
});
|
||||
setIsDeleteModalOpen(true);
|
||||
};
|
||||
|
||||
// const openDeleteDatasetModal = () => {
|
||||
// setIsDeleteDatasetModalOpen(true);
|
||||
// };
|
||||
const handleDelete = () => {
|
||||
if (deleteType === "voice") {
|
||||
voices.delete(deleteItem);
|
||||
} else if (deleteType === "dataset") datasets.delete(deleteItem);
|
||||
setDeleteItem("");
|
||||
setDeleteType("");
|
||||
};
|
||||
|
||||
const closeDeleteDatasetModal = () => {
|
||||
setIsDeleteDatasetModalOpen(false);
|
||||
const closeDeleteModal = () => {
|
||||
setIsDeleteModalOpen(false);
|
||||
setDeleteItem("");
|
||||
setDeleteType("");
|
||||
};
|
||||
|
||||
const history = useHistory();
|
||||
@@ -57,15 +72,16 @@ function VoiceDesignerMainPage({ inferenceJobsByCategory }: { inferenceJobsByCat
|
||||
const voiceClick =
|
||||
(todo: any, type: string) =>
|
||||
({ target }: { target: any }) => {
|
||||
let voiceToken = voices.list[target.name.split(",")[0].split(":")[1]].voice_token;
|
||||
let voiceToken =
|
||||
voices.list[target.name.split(",")[0].split(":")[1]].voice_token;
|
||||
todo(voiceToken, type);
|
||||
};
|
||||
|
||||
|
||||
const datasetClick =
|
||||
(todo: any, type: string) =>
|
||||
({ target }: { target: any }) => {
|
||||
let datasetToken = datasets.list[target.name.split(",")[0].split(":")[1]].dataset_token;
|
||||
let datasetToken =
|
||||
datasets.list[target.name.split(",")[0].split(":")[1]].dataset_token;
|
||||
todo(datasetToken, type);
|
||||
};
|
||||
|
||||
@@ -84,7 +100,7 @@ function VoiceDesignerMainPage({ inferenceJobsByCategory }: { inferenceJobsByCat
|
||||
label: "Delete",
|
||||
small: true,
|
||||
variant: "danger",
|
||||
onClick: datasetClick(datasets.delete, "dataset"),
|
||||
onClick: datasetClick(openDeleteModal, "dataset"),
|
||||
},
|
||||
],
|
||||
name: dataset.title,
|
||||
@@ -106,7 +122,7 @@ function VoiceDesignerMainPage({ inferenceJobsByCategory }: { inferenceJobsByCat
|
||||
label: "Delete",
|
||||
small: true,
|
||||
variant: "danger",
|
||||
onClick: voiceClick(voices.delete, "voice"),
|
||||
onClick: voiceClick(openDeleteModal, "voice"),
|
||||
},
|
||||
{
|
||||
label: "Use Voice",
|
||||
@@ -181,31 +197,13 @@ function VoiceDesignerMainPage({ inferenceJobsByCategory }: { inferenceJobsByCat
|
||||
</Panel>
|
||||
</Container>
|
||||
|
||||
{/* Delete Voice Modal */}
|
||||
{/* Delete Modal */}
|
||||
<Modal
|
||||
show={isDeleteVoiceModalOpen}
|
||||
handleClose={closeDeleteVoiceModal}
|
||||
title="Delete Voice"
|
||||
content={
|
||||
<p>
|
||||
Are you sure you want to delete "[Voice Name]"?
|
||||
<br />
|
||||
This cannot be undone.
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
{/* Delete Dataset Modal */}
|
||||
<Modal
|
||||
show={isDeleteDatasetModalOpen}
|
||||
handleClose={closeDeleteDatasetModal}
|
||||
title="Delete Dataset"
|
||||
content={
|
||||
<p>
|
||||
Are you sure you want to delete "[Dataset Name]"?
|
||||
<br />
|
||||
This cannot be undone.
|
||||
</p>
|
||||
}
|
||||
show={isDeleteModalOpen}
|
||||
handleClose={closeDeleteModal}
|
||||
title={deleteText.title}
|
||||
content={<p>{deleteText.text}</p>}
|
||||
onConfirm={handleDelete}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -19,7 +19,7 @@ import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapp
|
||||
import Container from "components/common/Container/Container";
|
||||
import TextArea from "components/common/TextArea";
|
||||
import { Button } from "components/common";
|
||||
import { SessionTtsInferenceResultList } from "v2/view/_common/SessionTtsInferenceResultsList";
|
||||
import { SessionVoiceDesignerInferenceResultsList } from "v2/view/_common/SessionVoiceDesignerInferenceResultsList";
|
||||
import {
|
||||
FrontendInferenceJobType,
|
||||
InferenceJob,
|
||||
@@ -230,7 +230,7 @@ export default function VoiceDesignerUseVoicePage(
|
||||
Session TTS Results
|
||||
</h4>
|
||||
<div className="d-flex flex-column gap-3 session-tts-section">
|
||||
<SessionTtsInferenceResultList
|
||||
<SessionVoiceDesignerInferenceResultsList
|
||||
inferenceJobs={
|
||||
props.inferenceJobsByCategory.get(
|
||||
FrontendInferenceJobType.VoiceDesignerTts
|
||||
|
||||
+3
-1
@@ -4,6 +4,7 @@ interface StepperControlsProps {
|
||||
onBack: () => void;
|
||||
onNext: () => void;
|
||||
onCreate: () => void;
|
||||
createDisabled: boolean;
|
||||
steps: string[];
|
||||
currentStep: number;
|
||||
}
|
||||
@@ -14,6 +15,7 @@ const StepperControls: React.FC<StepperControlsProps> = ({
|
||||
steps,
|
||||
currentStep,
|
||||
onCreate,
|
||||
createDisabled
|
||||
}) => {
|
||||
return (
|
||||
<div className="p-3 pb-4 px-lg-4 pt-0 d-flex gap-3">
|
||||
@@ -30,7 +32,7 @@ const StepperControls: React.FC<StepperControlsProps> = ({
|
||||
)}
|
||||
|
||||
{currentStep === 1 && (
|
||||
<button className="btn btn-primary w-100" onClick={onCreate}>
|
||||
<button disabled={ createDisabled } className="btn btn-primary w-100" onClick={onCreate}>
|
||||
Create Voice
|
||||
</button>
|
||||
)}
|
||||
|
||||
+7
-2
@@ -27,10 +27,11 @@ interface Props {
|
||||
formIsCleared: boolean;
|
||||
setFormIsCleared: (cleared: boolean) => void;
|
||||
setCanConvert: (canConvert: boolean) => void;
|
||||
setAudioSamplesReady: (ready: boolean) => void;
|
||||
changeConvertIdempotencyToken: () => void;
|
||||
}
|
||||
|
||||
function VoiceDesignerUploadComponent({ changeConvertIdempotencyToken, datasetToken, setCanConvert, setFormIsCleared }: Props) {
|
||||
function VoiceDesignerUploadComponent({ changeConvertIdempotencyToken, datasetToken, setCanConvert, setFormIsCleared, setAudioSamplesReady }: Props) {
|
||||
const [isUploadDisabled, setIsUploadDisabled] = useState<boolean>(false);
|
||||
|
||||
const files = useUploadedFiles((state: any) => state.files);
|
||||
@@ -38,6 +39,7 @@ function VoiceDesignerUploadComponent({ changeConvertIdempotencyToken, datasetTo
|
||||
const audioLinks = useUploadedFiles((state: any) => state.audioLinks);
|
||||
const setAudioLinks = useUploadedFiles((state: any) => state.setAudioLinks);
|
||||
|
||||
|
||||
// Auto generated
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
// const [idempotencyToken, setIdempotencyToken] = useState(uuidv4());
|
||||
@@ -53,7 +55,9 @@ function VoiceDesignerUploadComponent({ changeConvertIdempotencyToken, datasetTo
|
||||
file: file,
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
})
|
||||
.then(res => {});
|
||||
.then(res => {
|
||||
setAudioSamplesReady(true);
|
||||
});
|
||||
|
||||
setFormIsCleared(false);
|
||||
setCanConvert(false);
|
||||
@@ -100,6 +104,7 @@ function VoiceDesignerUploadComponent({ changeConvertIdempotencyToken, datasetTo
|
||||
<div className="d-flex flex-column gap-3">
|
||||
{/* Usage refer to https://github.com/KarimMokhtar/react-drag-drop-files */}
|
||||
<FileUploader
|
||||
key={datasetToken || uuidv4()}
|
||||
handleChange={handleChange}
|
||||
name="file"
|
||||
types={FILE_TYPES}
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ import React, { useState } from "react";
|
||||
import VoiceDesignerUploadComponent from "../VoiceDesignerUploadComponent";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
function UploadSamples({ audioProps, datasetToken }: { audioProps: any, datasetToken?: string }) {
|
||||
function UploadSamples({ audioProps, datasetToken, setAudioSamplesReady }: { audioProps: any, datasetToken?: string, setAudioSamplesReady:any }) {
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
const [convertLoading, setConvertLoading] = useState(false);
|
||||
const [canConvert, setCanConvert] = useState(false);
|
||||
@@ -25,7 +25,7 @@ function UploadSamples({ audioProps, datasetToken }: { audioProps: any, datasetT
|
||||
<div>
|
||||
<label className="sub-title">Upload Audio</label>
|
||||
<div className="d-flex flex-column gap-3 upload-component">
|
||||
<VoiceDesignerUploadComponent {...{ changeConvertIdempotencyToken, datasetToken, formIsCleared, setCanConvert, setFormIsCleared, setMediaUploadToken }}/>
|
||||
<VoiceDesignerUploadComponent {...{ changeConvertIdempotencyToken, datasetToken, formIsCleared, setCanConvert, setFormIsCleared, setMediaUploadToken, setAudioSamplesReady }}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user