diff --git a/packages/components/src/api/ApiConfig.ts b/packages/components/src/api/ApiConfig.ts index 9f1cd38e14..13d937979b 100644 --- a/packages/components/src/api/ApiConfig.ts +++ b/packages/components/src/api/ApiConfig.ts @@ -492,10 +492,14 @@ class ApiConfig { } // =============== Motion Capture / mocap =============== - enqueueVideoMotionCapture(): string{ - return `${this.getApiOrigin()}/v1/mocap/mocapnet/create` + enqueueVideoMotionCapture(): string { + return `${this.getApiOrigin()}/v1/mocap/mocapnet/create`; } + // =============== Convert FBX to glTF =============== + enqueueFbxToGltf(): string { + return `${this.getApiOrigin()}/v1/conversion/enqueue_fbx_to_gltf`; + } // =============== Generic Model Downloads =============== @@ -615,4 +619,4 @@ class ApiConfig { } } -export { ApiConfig }; \ No newline at end of file +export { ApiConfig }; diff --git a/packages/components/src/api/file_conversion/EnqueueFbxToGltf.ts b/packages/components/src/api/file_conversion/EnqueueFbxToGltf.ts new file mode 100644 index 0000000000..998cc6f987 --- /dev/null +++ b/packages/components/src/api/file_conversion/EnqueueFbxToGltf.ts @@ -0,0 +1,49 @@ +import { ApiConfig } from "../ApiConfig"; + +export interface EnqueueFbxToGltfRequest { + media_file_token: string; + uuid_idempotency_token: string; +} + +export interface EnqueueFbxToGltfResponse { + success: boolean; + inference_job_token?: string; +} + +const EnqueueFbxToGltfIsSuccess = (response: EnqueueFbxToGltfResponse) => + response.inference_job_token; +const EnqueueFbxToGltfIsError = (response: EnqueueFbxToGltfResponse) => + !response.inference_job_token; + +export { EnqueueFbxToGltfIsSuccess, EnqueueFbxToGltfIsError }; + +export async function EnqueueFbxToGltf( + request: EnqueueFbxToGltfRequest +): Promise { + const endpoint = new ApiConfig().enqueueFbxToGltf(); + + return await fetch(endpoint, { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + credentials: "include", + body: JSON.stringify(request), + }) + .then(res => res.json()) + .then(res => { + if (!res) { + return { success: false }; + } + + if (res && "success" in res) { + return res; + } else { + return { success: false }; + } + }) + .catch(e => { + return { success: false }; + }); +} diff --git a/packages/fakeyou.com/src/App.tsx b/packages/fakeyou.com/src/App.tsx index aacbd5d7ea..36a04d9c49 100644 --- a/packages/fakeyou.com/src/App.tsx +++ b/packages/fakeyou.com/src/App.tsx @@ -60,10 +60,7 @@ import { VoiceConversionModelUploadJob } from "@storyteller/components/src/jobs/ import { VoiceConversionModelListItem } from "@storyteller/components/src/api/voice_conversion/ListVoiceConversionModels"; import HttpBackend from "i18next-http-backend"; -import { - InferenceJobs, - SessionProvider, -} from "components/providers"; +import { InferenceJobs, SessionProvider } from "components/providers"; // NB: We're transitioning over to this instance of i18n-next that loads translations over HTTP from Json Files. // The old i18n-next instance (see below) bakes in translations into the compiled javascript blob. @@ -207,6 +204,7 @@ function initInferenceJobsByCategoryMap(): Map< ); inferenceJobsByCategory.set(FrontendInferenceJobType.VoiceDesignerTts, []); inferenceJobsByCategory.set(FrontendInferenceJobType.VideoMotionCapture, []); + inferenceJobsByCategory.set(FrontendInferenceJobType.ConvertFbxtoGltf, []); return inferenceJobsByCategory; } @@ -404,11 +402,11 @@ class App extends React.Component { Accept: "application/json", }, }) - .then((_raw_response) => { + .then(_raw_response => { this.querySession(); this.querySessionSubscriptions(); }) - .catch((e) => { + .catch(e => { /* Ignore. */ }); }; @@ -451,7 +449,7 @@ class App extends React.Component { if (GetModelInferenceJobStatusIsOk(lookupResult)) { let updatedJobs: Array = []; - this.state.inferenceJobs.forEach((existingJob) => { + this.state.inferenceJobs.forEach(existingJob => { if ( existingJob.jobToken !== lookupResult.state!.job_token || !jobStateCanChange(existingJob.jobState) @@ -511,8 +509,8 @@ class App extends React.Component { Accept: "application/json", }, }) - .then((res) => res.json()) - .then((response) => { + .then(res => res.json()) + .then(response => { const jobResponse: TtsInferenceJobStateResponsePayload = response; if (jobResponse === undefined || jobResponse.state === undefined) { @@ -521,7 +519,7 @@ class App extends React.Component { let updatedJobs: Array = []; - this.state.ttsInferenceJobs.forEach((existingJob) => { + this.state.ttsInferenceJobs.forEach(existingJob => { if ( existingJob.jobToken !== jobResponse.state!.job_token || !jobStateCanChange(existingJob.jobState) @@ -538,7 +536,7 @@ class App extends React.Component { ttsInferenceJobs: updatedJobs, }); }) - .catch((e) => { + .catch(e => { /* Ignore. */ }); }; @@ -563,8 +561,8 @@ class App extends React.Component { Accept: "application/json", }, }) - .then((res) => res.json()) - .then((response) => { + .then(res => res.json()) + .then(response => { const jobResponse: TtsModelUploadJobStateResponsePayload = response; if (jobResponse === undefined || jobResponse.state === undefined) { @@ -572,7 +570,7 @@ class App extends React.Component { } let updatedJobs: Array = []; - this.state.ttsModelUploadJobs.forEach((existingJob) => { + this.state.ttsModelUploadJobs.forEach(existingJob => { if ( existingJob.jobToken !== jobResponse.state!.job_token || !jobStateCanChange(existingJob.jobState) @@ -589,7 +587,7 @@ class App extends React.Component { ttsModelUploadJobs: updatedJobs, }); }) - .catch((e) => { + .catch(e => { /* Ignore. */ }); }; @@ -614,8 +612,8 @@ class App extends React.Component { Accept: "application/json", }, }) - .then((res) => res.json()) - .then((response) => { + .then(res => res.json()) + .then(response => { const jobResponse: W2lInferenceJobStateResponsePayload = response; if (jobResponse === undefined || jobResponse.state === undefined) { @@ -623,7 +621,7 @@ class App extends React.Component { } let updatedJobs: Array = []; - this.state.w2lInferenceJobs.forEach((existingJob) => { + this.state.w2lInferenceJobs.forEach(existingJob => { if ( existingJob.jobToken !== jobResponse.state!.job_token || !jobStateCanChange(existingJob.jobState) @@ -640,7 +638,7 @@ class App extends React.Component { w2lInferenceJobs: updatedJobs, }); }) - .catch((e) => { + .catch(e => { /* Ignore. */ }); }; @@ -665,8 +663,8 @@ class App extends React.Component { Accept: "application/json", }, }) - .then((res) => res.json()) - .then((response) => { + .then(res => res.json()) + .then(response => { const jobResponse: W2lTemplateUploadJobStateResponsePayload = response; if (jobResponse === undefined || jobResponse.state === undefined) { @@ -675,7 +673,7 @@ class App extends React.Component { let updatedJobs: Array = []; - this.state.w2lTemplateUploadJobs.forEach((existingJob) => { + this.state.w2lTemplateUploadJobs.forEach(existingJob => { if ( existingJob.jobToken !== jobResponse.state!.job_token || !jobStateCanChange(existingJob.jobState) @@ -694,7 +692,7 @@ class App extends React.Component { w2lTemplateUploadJobs: updatedJobs, }); }) - .catch((e) => { + .catch(e => { /* Ignore. */ }); }; @@ -714,7 +712,7 @@ class App extends React.Component { if (GetRemoteDownloadJobStatusIsOk(lookupResult)) { let updatedJobs: Array = []; - this.state.vocoderUploadJobs.forEach((existingJob) => { + this.state.vocoderUploadJobs.forEach(existingJob => { if ( existingJob.jobToken !== lookupResult.state!.job_token || !jobStateCanChange(existingJob.jobState) @@ -748,7 +746,7 @@ class App extends React.Component { if (GetRemoteDownloadJobStatusIsOk(lookupResult)) { let updatedJobs: Array = []; - this.state.voiceConversionModelUploadJobs.forEach((existingJob) => { + this.state.voiceConversionModelUploadJobs.forEach(existingJob => { if ( existingJob.jobToken !== lookupResult.state!.job_token || !jobStateCanChange(existingJob.jobState) @@ -770,37 +768,37 @@ class App extends React.Component { }; pollJobs = () => { - this.state.inferenceJobs.forEach((job) => { + this.state.inferenceJobs.forEach(job => { if (jobStateCanChange(job.jobState)) { this.checkInferenceJob(job.jobToken); } }); - this.state.ttsInferenceJobs.forEach((job) => { + this.state.ttsInferenceJobs.forEach(job => { if (jobStateCanChange(job.jobState)) { this.checkTtsJob(job.jobToken); } }); - this.state.w2lInferenceJobs.forEach((job) => { + this.state.w2lInferenceJobs.forEach(job => { if (jobStateCanChange(job.jobState)) { this.checkW2lJob(job.jobToken); } }); - this.state.ttsModelUploadJobs.forEach((job) => { + this.state.ttsModelUploadJobs.forEach(job => { if (jobStateCanChange(job.jobState)) { this.checkTtsModelUploadJob(job.jobToken); } }); - this.state.w2lTemplateUploadJobs.forEach((job) => { + this.state.w2lTemplateUploadJobs.forEach(job => { if (jobStateCanChange(job.jobState)) { this.checkW2lTemplateUploadJob(job.jobToken); } }); - this.state.vocoderUploadJobs.forEach((job) => { + this.state.vocoderUploadJobs.forEach(job => { if (jobStateCanChange(job.jobState)) { this.checkVocoderUploadJob(job.jobToken); } }); - this.state.voiceConversionModelUploadJobs.forEach((job) => { + this.state.voiceConversionModelUploadJobs.forEach(job => { if (jobStateCanChange(job.jobState)) { this.checkVoiceConversionModelUploadJob(job.jobToken); } @@ -843,7 +841,7 @@ class App extends React.Component { querySession: this.querySession, querySubscriptions: this.querySessionSubscriptions, sessionWrapper: this.state.sessionWrapper, - sessionFetched: this.state.sessionFetched + sessionFetched: this.state.sessionFetched, }} > diff --git a/packages/fakeyou.com/src/v2/view/pages/fbx_to_gltf/FbxToGltfPage.tsx b/packages/fakeyou.com/src/v2/view/pages/fbx_to_gltf/FbxToGltfPage.tsx index 14a2999f4c..2ec760b9ce 100644 --- a/packages/fakeyou.com/src/v2/view/pages/fbx_to_gltf/FbxToGltfPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/fbx_to_gltf/FbxToGltfPage.tsx @@ -14,6 +14,11 @@ import { v4 as uuidv4 } from "uuid"; import { UploadMedia } from "@storyteller/components/src/api/media_files/UploadMedia"; import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob"; import FbxToGltfJobList from "./components/FbxToGltfJobList"; +import { + EnqueueFbxToGltf, + EnqueueFbxToGltfIsSuccess, + EnqueueFbxToGltfIsError, +} from "@storyteller/components/src/api/file_conversion/EnqueueFbxToGltf"; interface FbxToGltfPageProps { enqueueInferenceJob: ( @@ -32,12 +37,16 @@ export default function FbxToGltfPage({ mediaToken: mediaToken ?? undefined, }); const fileProps = useFile({}); + const [isSubmitting, setIsSubmitting] = useState(false); + const [isWorking, setIsWorking] = useState(false); const { inferenceJobs } = useInferenceJobs( - FrontendInferenceJobType.VideoMotionCapture + FrontendInferenceJobType.ConvertFbxtoGltf ); const hasConversionJobs = inferenceJobs && inferenceJobs.length > 0; + console.log("inferenceJobs", inferenceJobs); + useEffect(() => { // If there's a mediaToken, automatically prepare the file for conversion if (mediaToken && presetFile) { @@ -50,14 +59,6 @@ export default function FbxToGltfPage({ history.push("/fbx-to-gltf"); }; - async function EnqueueFbxToGltf(file: any) { - return new Promise(resolve => { - setTimeout(() => { - console.log("EnqueueFbxToGltf", file); - }, 1000); - }); - } - const makeFBXUploadRequest = () => ({ uuid_idempotency_token: uuidv4(), file: fileProps.file, @@ -69,57 +70,75 @@ export default function FbxToGltfPage({ try { const res = await UploadMedia(makeFBXUploadRequest()); if (res.success && res.media_file_token) { - return { upload_token: res.upload_token }; + return { upload_token: res.media_file_token }; } - // Handle failure scenario - perhaps throw an error or return a specific result console.error("Upload failed:", res); return null; } catch (error) { console.error("Error in upload:", error); - // Handle the error appropriately return null; } }; - const EnqueueConvert = async (params: any) => { - let request: any = { - uuid_idempotency_token: uuidv4(), - file_source: undefined, - upload_token: params.upload_token, - }; - - const response = await EnqueueFbxToGltf(request); - - console.log("Response: ", response); - - // if (response.inference_job_token) { - // enqueueInferenceJob( - // response.inference_job_token, - // FrontendInferenceJobType.ConvertFbxtoGltf - // ); - // } - }; - - const uploadAndConvert = async () => { + const submit = async () => { + if (isSubmitting) return; + setIsSubmitting(true); + setIsWorking(true); try { - const uploadResponse = await handleUploadFBX(); - if (uploadResponse && uploadResponse.upload_token) { - await EnqueueConvert({ - upload_token: uploadResponse.upload_token, - }); + let uploadToken = mediaToken; + + // If no media token, upload the file first + if (!mediaToken && fileProps.file) { + const uploadResponse = await handleUploadFBX(); + if (uploadResponse && uploadResponse.upload_token) { + uploadToken = uploadResponse.upload_token; + } else { + console.error("Failed to upload file. Response:", uploadResponse); + throw new Error("File upload failed"); + } + } + + // Enqueue the conversion job + if (uploadToken) { + await EnqueueConvert({ upload_token: uploadToken }); + } else { + console.error("No upload token available for conversion"); } } catch (error) { - console.error("Error uploading file: ", error); - // Handle upload error + console.error("Error in submit process: ", error); + } finally { + setIsWorking(false); + setIsSubmitting(false); } }; - const submit = async () => { - if (mediaToken && presetFile) { - EnqueueConvert({ upload_token: mediaToken }); - } else if (fileProps.file) { - await uploadAndConvert(); - } else { + const EnqueueConvert = async ({ upload_token }: any) => { + if (!upload_token) return false; + + try { + let request = { + uuid_idempotency_token: uuidv4(), + file_source: undefined, + media_file_token: upload_token, + }; + + const response = await EnqueueFbxToGltf(request); + + if (EnqueueFbxToGltfIsSuccess(response)) { + console.log("Enqueue successful"); + + if (response && response.inference_job_token) { + enqueueInferenceJob( + response.inference_job_token, + FrontendInferenceJobType.ConvertFbxtoGltf + ); + } + return true; + } else if (EnqueueFbxToGltfIsError(response)) { + throw new Error("Enqueue failed"); + } + } catch (error) { + console.error("Error in enqueueing conversion: ", error); return false; } }; @@ -179,7 +198,8 @@ export default function FbxToGltfPage({ icon={faArrowRightArrowLeft} label="Convert to glTF" onClick={submit} - disabled={!fileProps.file} + disabled={!mediaToken && !fileProps.file} + isLoading={isWorking} />