diff --git a/packages/components/src/api/ApiConfig.ts b/packages/components/src/api/ApiConfig.ts index 4dfaf491d5..7433c63b15 100644 --- a/packages/components/src/api/ApiConfig.ts +++ b/packages/components/src/api/ApiConfig.ts @@ -395,11 +395,11 @@ class ApiConfig { // =============== Generic Model Downloads =============== enqueueRemoteDownloadJob(): string { - return `${this.getApiOrigin()}/v1/remote_downloads/enqueue`; + return `${this.getApiOrigin()}/v1/remote_download/enqueue`; } getRemoteDownloadJobStatus(jobToken: string): string { - return `${this.getApiOrigin()}/v1/remote_downloads/job_status/${jobToken}`; + return `${this.getApiOrigin()}/v1/remote_download/job_status/${jobToken}`; } // =============== Generic Model Inference =============== diff --git a/packages/components/src/jobs/VoiceConversionModelUploadJob.ts b/packages/components/src/jobs/VoiceConversionModelUploadJob.ts new file mode 100644 index 0000000000..5c8da05d21 --- /dev/null +++ b/packages/components/src/jobs/VoiceConversionModelUploadJob.ts @@ -0,0 +1,58 @@ +import { RemoteDownloadJobStatus } from "../api/remote_downloads/GetRemoteDownloadJobStatus"; +import { JobState, jobStateFromString } from "./JobStates"; + +export class VoiceConversionModelUploadJob { + jobToken: string; + maybeDownloadedEntityType: string | undefined | null; + maybeDownloadedEntityToken: string | undefined | null; + jobState: JobState; + maybeExtraStatusDescription: string | null; + attemptCount: number; + + constructor( + jobToken: string, + status: string = 'unknown', + maybeExtraStatusDescription: string | null = null, + attemptCount: number = 0, + maybeDownloadedEntityType: string | undefined | null = undefined, + maybeDownloadedEntityToken: string | undefined | null = undefined + ) { + this.jobState = jobStateFromString(status); + this.maybeExtraStatusDescription = maybeExtraStatusDescription; + this.attemptCount = attemptCount; + this.jobToken = jobToken; + if (!!maybeDownloadedEntityType) { + this.maybeDownloadedEntityType = maybeDownloadedEntityType; + } + if (!!maybeDownloadedEntityToken) { + this.maybeDownloadedEntityToken = maybeDownloadedEntityToken; + } + } + + static fromResponse(response: RemoteDownloadJobStatus) : VoiceConversionModelUploadJob { + return new VoiceConversionModelUploadJob( + response.job_token, + response.status, + response.maybe_extra_status_description || null, + response.attempt_count || 0, + response.maybe_downloaded_entity_type, + response.maybe_downloaded_entity_token, + ); + } +} + +export interface VoiceConversionModelUploadJobStateResponsePayload { + success: boolean, + state?: VoiceConversionModelUploadJobState, +} + +export interface VoiceConversionModelUploadJobState { + job_token: string, + status: string, + maybe_extra_status_description: string | null, + attempt_count: number | undefined, + maybe_downloaded_entity_type: string | undefined | null, + maybe_downloaded_entity_token: string | undefined | null, + created_at: string, + updated_at: string, +} diff --git a/packages/fakeyou.com/src/App.tsx b/packages/fakeyou.com/src/App.tsx index b454e3b254..e041710e17 100644 --- a/packages/fakeyou.com/src/App.tsx +++ b/packages/fakeyou.com/src/App.tsx @@ -46,6 +46,7 @@ import { } from "./_i18n/AvailableLanguageMap"; import { InferenceJob } from "@storyteller/components/src/jobs/InferenceJob"; import { GetModelInferenceJobStatus, GetModelInferenceJobStatusIsOk } from "@storyteller/components/src/api/model_inference/GetModelInferenceJobStatus"; +import { VoiceConversionModelUploadJob } from "@storyteller/components/src/jobs/VoiceConversionModelUploadJob"; i18n .use(initReactI18next) // passes i18n down to react-i18next @@ -134,6 +135,7 @@ interface State { ttsModelUploadJobs: Array; w2lTemplateUploadJobs: Array; vocoderUploadJobs: Array; + voiceConversionModelUploadJobs: Array; // Current text entered textBuffer: string; @@ -194,6 +196,7 @@ class App extends React.Component { ttsModelUploadJobs: [], w2lTemplateUploadJobs: [], vocoderUploadJobs: [], + voiceConversionModelUploadJobs: [], textBuffer: "", }; @@ -593,6 +596,40 @@ class App extends React.Component { } }; + enqueueVoiceConversionModelUploadJob = (jobToken: string) => { + const newJob = new VoiceConversionModelUploadJob(jobToken); + let uploadJobs = this.state.voiceConversionModelUploadJobs.concat([newJob]); + + this.setState({ + voiceConversionModelUploadJobs: uploadJobs, + }); + }; + + checkVoiceConversionModelUploadJob = async (jobToken: string) => { + const lookupResult = await GetRemoteDownloadJobStatus(jobToken); + + if (GetRemoteDownloadJobStatusIsOk(lookupResult)) { + let updatedJobs: Array = []; + + this.state.voiceConversionModelUploadJobs.forEach((existingJob) => { + if ( + existingJob.jobToken !== lookupResult.state!.job_token || + !jobStateCanChange(existingJob.jobState) + ) { + updatedJobs.push(existingJob); + return; + } + + let updatedJob = VoiceConversionModelUploadJob.fromResponse(lookupResult.state!); + updatedJobs.push(updatedJob); + }); + + this.setState({ + voiceConversionModelUploadJobs: updatedJobs, + }); + } + }; + pollJobs = () => { this.state.inferenceJobs.forEach((job) => { if (jobStateCanChange(job.jobState)) { @@ -624,6 +661,11 @@ class App extends React.Component { this.checkVocoderUploadJob(job.jobToken); } }); + this.state.voiceConversionModelUploadJobs.forEach((job) => { + if (jobStateCanChange(job.jobState)) { + this.checkVoiceConversionModelUploadJob(job.jobToken); + } + }); }; setMigrationMode = (mode: MigrationMode) => { @@ -702,6 +744,8 @@ class App extends React.Component { w2lTemplateUploadJobs={this.state.w2lTemplateUploadJobs} enqueueVocoderUploadJob={this.enqueueVocoderUploadJob} vocoderUploadJobs={this.state.vocoderUploadJobs} + enqueueVoiceConversionModelUploadJob={this.enqueueVoiceConversionModelUploadJob} + voiceConversionModelUploadJobs={this.state.voiceConversionModelUploadJobs} textBuffer={this.state.textBuffer} setTextBuffer={this.setTextBuffer} clearTextBuffer={this.clearTextBuffer} diff --git a/packages/fakeyou.com/src/v2/view/PageContainer.tsx b/packages/fakeyou.com/src/v2/view/PageContainer.tsx index 84ee4fcbd8..3e7854d5a1 100644 --- a/packages/fakeyou.com/src/v2/view/PageContainer.tsx +++ b/packages/fakeyou.com/src/v2/view/PageContainer.tsx @@ -20,6 +20,7 @@ import { TtsModelDeletePage } from "./pages/tts/tts_model_delete/TtsModelDeleteP import { TtsModelEditPage } from "./pages/tts/tts_model_edit/TtsModelEditPage"; import { TtsModelUploadJob } from "@storyteller/components/src/jobs/TtsModelUploadJobs"; import { VocoderUploadJob } from "@storyteller/components/src/jobs/VocoderUploadJobs"; +import { VoiceConversionModelUploadJob } from "@storyteller/components/src/jobs/VoiceConversionModelUploadJob"; import { TtsModelViewPage } from "./pages/tts/tts_model_view/TtsModelViewPage"; import { TtsResultDeletePage } from "./pages/tts/tts_result_delete/TtsResultDeletePage"; import { TtsResultViewPage } from "./pages/tts/tts_result_view/TtsResultViewPage"; @@ -111,9 +112,14 @@ interface Props { enqueueW2lTemplateUploadJob: (jobToken: string) => void; w2lTemplateUploadJobs: Array; + // TODO: Begin to unify generic download jobs (vocoder, voice conversion, ...) enqueueVocoderUploadJob: (jobToken: string) => void; vocoderUploadJobs: Array; + // TODO: Begin to unify generic download jobs (vocoder, voice conversion, ...) + enqueueVoiceConversionModelUploadJob: (jobToken: string) => void; + voiceConversionModelUploadJobs: Array; + textBuffer: string; setTextBuffer: (textBuffer: string) => void; clearTextBuffer: () => void; @@ -378,8 +384,8 @@ class PageContainer extends React.Component { diff --git a/packages/fakeyou.com/src/v2/view/_common/SessionVoiceConversionModelUploadResultsList.tsx b/packages/fakeyou.com/src/v2/view/_common/SessionVoiceConversionModelUploadResultsList.tsx new file mode 100644 index 0000000000..8e90526e14 --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/_common/SessionVoiceConversionModelUploadResultsList.tsx @@ -0,0 +1,89 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import { JobState } from "@storyteller/components/src/jobs/JobStates"; +import { motion } from "framer-motion"; +import { container, sessionItem, item } from "../../../data/animation"; +import { VoiceConversionModelUploadJob } from "@storyteller/components/src/jobs/VoiceConversionModelUploadJob"; + +interface Props { + voiceConversionModelUploadJobs: Array; +} + +function SessionVoiceConversionModelUploadResultList(props: Props) { + let results: Array = []; + + props.voiceConversionModelUploadJobs.forEach((job) => { + if (!job.maybeDownloadedEntityToken) { + let stateDescription = "Pending..."; + + switch (job.jobState) { + case JobState.PENDING: + case JobState.UNKNOWN: + stateDescription = + job.maybeExtraStatusDescription == null + ? "Pending..." + : job.maybeExtraStatusDescription; + break; + case JobState.STARTED: + stateDescription = + job.maybeExtraStatusDescription == null + ? "Started..." + : job.maybeExtraStatusDescription; + break; + case JobState.ATTEMPT_FAILED: + stateDescription = `Failed ${job.attemptCount} attempt(s). Will retry...`; + break; + case JobState.COMPLETE_FAILURE: + case JobState.DEAD: + stateDescription = + "Failed Permanently. Please tell us in Discord so we can fix. :("; + break; + case JobState.COMPLETE_SUCCESS: + stateDescription = "Success!"; // Not sure why we're here instead of other branch! + break; + } + + results.push( +
+
{stateDescription}
+
+ ); + } else { + let permalink = `/voice_conversion/${job.maybeDownloadedEntityToken}`; + + results.push( +
+ + Complete! + + See & use voice conversion model + + +
+ ); + } + }); + + let title = ; + if (results.length !== 0) { + title = ( + + Voice Conversion Model Upload Status + + ); + } + + return ( + +
+
{title}
+
{results}
+
+
+ ); +} + +export { SessionVoiceConversionModelUploadResultList }; diff --git a/packages/fakeyou.com/src/v2/view/pages/upload/UploadVoiceConversionModel.tsx b/packages/fakeyou.com/src/v2/view/pages/upload/UploadVoiceConversionModel.tsx index 3ba621524e..80865d406a 100644 --- a/packages/fakeyou.com/src/v2/view/pages/upload/UploadVoiceConversionModel.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/upload/UploadVoiceConversionModel.tsx @@ -12,15 +12,15 @@ import { Link } from "react-router-dom"; import { WebUrl } from "../../../../common/WebUrl"; import { motion } from "framer-motion"; import { container, item, panel } from "../../../../data/animation"; -import { VocoderUploadJob } from "@storyteller/components/src/jobs/VocoderUploadJobs"; -import { SessionVocoderUploadResultList } from "../../_common/SessionVocoderUploadResultsList"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faMicrophoneStand } from "@fortawesome/pro-solid-svg-icons"; +import { VoiceConversionModelUploadJob } from "@storyteller/components/src/jobs/VoiceConversionModelUploadJob"; +import { SessionVoiceConversionModelUploadResultList } from "../../_common/SessionVoiceConversionModelUploadResultsList"; interface Props { sessionWrapper: SessionWrapper; enqueueVoiceConversionModelUploadJob: (jobToken: string) => void; - voiceConversionModelUploadJobs: Array; // TODO + voiceConversionModelUploadJobs: Array; } function UploadVoiceConversionModel(props: Props) { @@ -74,7 +74,7 @@ function UploadVoiceConversionModel(props: Props) { idempotency_token: idempotencyToken, title: title, download_url: downloadUrl, - generic_download_type: "hifigan", + generic_download_type: "so_vits_svc", }; const response = await EnqueueRemoteDownload(request); @@ -170,12 +170,9 @@ function UploadVoiceConversionModel(props: Props) { - - {/* - - */} ); }