Api2.6/ upload animations expressions (#511)

Co-authored-by: wil <wil@storyteller.ai>
This commit is contained in:
wilandwil authored and GitHub committed 2024-06-20 16:14:06 -04:00
1 parent 1b5f6356e2
commit 122f39bd25
16 files changed
+99 -227

No files matched your search

+1 -5
View File
@@ -137,7 +137,7 @@ export class ApiManager {
endpoint: string;
formRecord: Record<string, string>;
uuid: string;
blob?: Blob;
blob?: Blob | File;
blobFileName?: string;
}): Promise<T> {
const formData = new FormData();
@@ -150,10 +150,6 @@ export class ApiManager {
} else if (blob) {
formData.append("file", blob);
}
// return this.fetch<FormData, T>(endpoint, {
// method: "POST",
// body: formData,
// });
return this.fetchMultipartFormData<T>(endpoint, {
method: "POST",
body: formData,
+8 -5
View File
@@ -1,5 +1,5 @@
import { ApiManager, ApiResponse } from "./ApiManager";
import { Visibility } from "~/enums";
import { FilterEngineCategories, Visibility } from "~/enums";
export class MediaUploadApi extends ApiManager {
private async Upload({
@@ -10,7 +10,7 @@ export class MediaUploadApi extends ApiManager {
options,
}: {
endpoint: string;
blob: Blob;
blob: Blob | File;
fileName: string;
uuid: string;
options: Record<string, string | number | undefined>;
@@ -87,17 +87,19 @@ export class MediaUploadApi extends ApiManager {
}
public async UploadNewEngineAsset({
blob,
file,
fileName,
uuid,
engine_category,
maybe_animation_type,
maybe_duration_millis,
maybe_title,
maybe_visibility = Visibility.Public,
}: {
blob: Blob;
file: File;
fileName: string;
uuid: string;
engine_category: FilterEngineCategories;
maybe_animation_type?: string;
maybe_duration_millis?: number;
maybe_title?: string;
@@ -105,12 +107,13 @@ export class MediaUploadApi extends ApiManager {
}): Promise<ApiResponse<string>> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/upload/new_engine_asset`;
const options: Record<string, string | number | undefined> = {
engine_category,
maybe_title,
maybe_visibility: maybe_visibility?.toString(),
maybe_animation_type,
maybe_duration_millis,
};
return this.Upload({ endpoint, blob, fileName, uuid, options });
return this.Upload({ endpoint, blob: file, fileName, uuid, options });
}
public async UploadNewScene({
@@ -1,7 +1,7 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faFileArrowUp, faFileAudio } from "@fortawesome/pro-solid-svg-icons";
import { P } from "~/components";
import { getFileName } from "./utils/getFileName";
import { getFileName } from "~/utilities";
interface Props {
file: File | null;
@@ -14,21 +14,13 @@ export const UploadAssetError = ({
return (
<>
{isAssetError
? "There was a problem uploading your object."
: "There was a problem creating art for your object"}
? "There was a problem uploading your file."
: "There was a problem uploading the thumbnail for your file."}
<div className="mt-6 flex justify-end gap-2">
<Button
{...{
onClick: onCancel,
variant: "secondary",
}}>
<Button onClick={onCancel} variant="secondary">
Cancel
</Button>
<Button
{...{
onClick: onRetry,
variant: "primary",
}}>
<Button onClick={onRetry} variant="primary">
Try again
</Button>
</div>
@@ -1,59 +1,20 @@
import { FileUploader } from "react-drag-drop-files";
import { Button } from "~/components";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSpinnerThird } from "@fortawesome/pro-solid-svg-icons";
import { DragAndDropZone } from "./DragAndDropZone";
import { UploadMediaResponse } from "~/components/signaled/UploadAudioButtonDialogue/utilities";
import { useState } from "react";
enum UploadState {
"init",
"staged",
"uploading",
"uploaded",
"error",
}
interface Props {
title: string;
fileTypes: string[];
setToken?: (token: string) => void;
uploadFile?: (file: File) => Promise<UploadMediaResponse>;
file: File | null;
setFile: (file: File | null) => void;
}
export const UploadFile = ({
fileTypes,
title,
setToken,
uploadFile,
file,
setFile,
}: Props) => {
const [uploadState, setUploadState] = useState<UploadState>(UploadState.init);
const submitUpload = (file: File | null) => {
if (file && uploadFile) {
setUploadState(UploadState.uploading);
uploadFile(file).then((res: UploadMediaResponse) => {
if ("media_file_token" in res) {
setUploadState(UploadState.uploaded);
setToken && setToken(res.media_file_token);
} else {
setUploadState(UploadState.error);
}
});
}
};
export const UploadFile = ({ fileTypes, title, file, setFile }: Props) => {
return (
<div className="flex w-full flex-col gap-3">
{title}
<FileUploader
handleChange={(file: File | null) => {
setFile(file);
setUploadState(file ? UploadState.staged : UploadState.init);
}}
name="file"
types={fileTypes}
@@ -61,19 +22,6 @@ export const UploadFile = ({
>
<DragAndDropZone file={file} fileTypes={fileTypes} />
</FileUploader>
{!!uploadFile && (
<div className="flex justify-end gap-3">
<Button
onClick={() => submitUpload(file)}
disabled={uploadState !== UploadState.staged}
>
Upload
{uploadState === UploadState.uploading && (
<FontAwesomeIcon icon={faSpinnerThird} spin />
)}
</Button>
</div>
)}
</div>
);
};
@@ -1,7 +1,7 @@
import { Button, H6, Input, ListDropdown } from "~/components";
import { UploadFile } from "./UploadFile";
import { useState } from "react";
import { MediaFileAnimationType } from "~/api/media_files/UploadNewEngineAsset";
import { MediaFileAnimationType } from "~/enums";
interface Props {
title: string;
@@ -1,30 +0,0 @@
import { Button } from "~/components";
interface Props {
onCancel: () => void;
onRetry: () => void;
}
export const UploadLoaderError = ({ onCancel, onRetry }: Props) => {
return (
<>
There was a problem loading your object. Try another.
<div className="mt-6 flex justify-end gap-2">
<Button
{...{
onClick: onCancel,
variant: "secondary",
}}>
Cancel
</Button>
<Button
{...{
onClick: onRetry,
variant: "primary",
}}>
Try again
</Button>
</div>
</>
);
};
@@ -1,16 +0,0 @@
.obj-uploader-modal-load-view {
.uploader-dots {
background: transparent;
margin-bottom: 1rem;
}
.uploader-message {
width: 100%;
opacity: 0.5;
text-align: center;
}
}
.object-preview-canvas {
width: 100%;
overflow: hidden;
}
@@ -1,28 +1,23 @@
import { useCallback, useState } from "react";
import { useEffect, useState } from "react";
import { v4 as uuidv4 } from "uuid";
import { TransitionDialogue, LoadingDots } from "~/components";
import "./UploadModal.scss";
import { UploadLoaderError } from "./UploadLoaderError";
import { UploadAssetError } from "./UploadAssetError";
import { UploadSuccess } from "./UploadSuccess";
import { UploadFiles } from "./UploadFiles";
import {
MediaFileAnimationType,
MediaFileEngineCategory,
UploadNewEngineAsset,
} from "~/api/media_files/UploadNewEngineAsset";
import { v4 as uuidv4 } from "uuid";
import { UploadMedia } from "~/api/media_files/UploadMedia";
import { EditCoverImage } from "~/api/media_files/EditCoverImage";
import { MediaFilesApi, MediaUploadApi } from "~/Classes/ApiManager";
import { FilterEngineCategories, MediaFileAnimationType } from "~/enums";
import { getFileName } from "~/utilities";
interface Props {
closeModal: () => void;
onClose: () => void;
onSuccess: () => void;
isOpen: boolean;
title: string;
fileTypes: string[];
typeOptions: { [key: string]: string }[];
type: MediaFileEngineCategory;
type: FilterEngineCategories;
}
enum UploaderState {
@@ -34,11 +29,9 @@ enum UploaderState {
assetError,
coverCreateError,
coverSetError,
loaderError,
}
export function UploadModalMovement({
closeModal,
isOpen,
onClose,
onSuccess,
@@ -49,9 +42,15 @@ export function UploadModalMovement({
}: Props) {
const [objUploadStatus, setObjUploadStatus] = useState(UploaderState.ready);
const resetModalState = useCallback(() => {
const resetModalState = () => {
setObjUploadStatus(UploaderState.ready);
}, []);
};
useEffect(() => {
if (isOpen) {
resetModalState();
}
}, [isOpen]);
const onSubmit = async ({
title,
@@ -66,54 +65,62 @@ export function UploadModalMovement({
length: number;
thumbnailFile: File | null;
}) => {
const res = await UploadNewEngineAsset({
engine_category: type,
setObjUploadStatus(UploaderState.uploadingAsset);
const mediaUploadApi = new MediaUploadApi();
const assetReponse = await mediaUploadApi.UploadNewEngineAsset({
file: assetFile,
maybe_animation_type: typeOption as MediaFileAnimationType,
fileName: assetFile.name,
engine_category: type,
maybe_animation_type: typeOption,
maybe_duration_millis: length,
maybe_title: title,
uuid_idempotency_token: uuidv4(),
uuid: uuidv4(),
});
if (!("media_file_token" in res)) {
if (!assetReponse.success || !assetReponse.data) {
setObjUploadStatus(UploaderState.assetError);
return;
}
const assetToken = res.media_file_token;
const assetToken = assetReponse.data;
if (!thumbnailFile) {
setObjUploadStatus(UploaderState.success);
return;
}
const resp = await UploadMedia({
uuid_idempotency_token: uuidv4(),
file: thumbnailFile,
source: title,
setObjUploadStatus(UploaderState.uploadingCover);
const thumbnailResponse = await mediaUploadApi.UploadImage({
uuid: uuidv4(),
blob: thumbnailFile,
fileName: getFileName(thumbnailFile),
maybe_title: "thumbnail_" + title,
});
if (!("media_file_token" in resp)) {
setObjUploadStatus(UploaderState.uploadingCover);
if (!thumbnailResponse.success || !thumbnailResponse.data) {
setObjUploadStatus(UploaderState.coverCreateError);
return;
}
const thumbnailToken = resp.media_file_token;
const editRes = await EditCoverImage(assetToken, {
cover_image_media_file_token: thumbnailToken,
setObjUploadStatus(UploaderState.settingCover);
const thumbnailToken = thumbnailResponse.data;
const mediaFilesApi = new MediaFilesApi();
const setThumbnailResponse = await mediaFilesApi.UpdateCoverImage({
mediaFileToken: assetToken,
imageToken: thumbnailToken,
});
if (!editRes.success) {
setObjUploadStatus(UploaderState.uploadingCover);
if (!setThumbnailResponse.success) {
setObjUploadStatus(UploaderState.coverSetError);
return;
}
setObjUploadStatus(UploaderState.success);
};
const objUploaderContent = () => {
const ObjUploaderContent = () => {
switch (objUploadStatus) {
case UploaderState.ready:
return (
<UploadFiles
title={title}
fileTypes={fileTypes}
onClose={() => {
closeModal();
onClose();
}}
onClose={onClose}
typeOptions={typeOptions}
onSubmit={onSubmit}
/>
@@ -122,20 +129,18 @@ export function UploadModalMovement({
case UploaderState.uploadingCover:
case UploaderState.settingCover:
return (
<div {...{ className: "obj-uploader-modal-load-view" }}>
<LoadingDots {...{ className: "uploader-dots" }} />
<div {...{ className: "uploader-message" }}>Uploading...</div>
</div>
<>
<LoadingDots className="mb-1 bg-transparent" />
<div className="w-100 text-center opacity-50">Uploading...</div>
</>
);
case UploaderState.success:
return (
<UploadSuccess
title={title}
onOk={() => {
closeModal();
onClose();
onSuccess();
setTimeout(() => setObjUploadStatus(UploaderState.ready), 0);
onClose();
}}
/>
);
@@ -144,50 +149,19 @@ export function UploadModalMovement({
case UploaderState.coverSetError:
return (
<UploadAssetError
onCancel={() => {
closeModal();
onClose();
setTimeout(() => setObjUploadStatus(UploaderState.ready), 0);
}}
onCancel={onClose}
onRetry={() => {
switch (objUploadStatus) {
case UploaderState.assetError: {
break;
}
case UploaderState.coverCreateError: {
break;
}
}
}}
isAssetError={objUploadStatus === UploaderState.assetError}
/>
);
case UploaderState.loaderError:
return (
<UploadLoaderError
onCancel={() => {
closeModal();
onClose();
setTimeout(() => setObjUploadStatus(UploaderState.ready), 0);
}}
onRetry={() => {
setObjUploadStatus(UploaderState.ready);
resetModalState();
}}
isAssetError={objUploadStatus === UploaderState.assetError}
/>
);
}
};
return (
<TransitionDialogue
{...{
isOpen,
onClose,
title: title,
}}
>
{objUploaderContent()}
<TransitionDialogue isOpen={isOpen} onClose={onClose} title={title}>
<ObjUploaderContent />
</TransitionDialogue>
);
}
@@ -7,20 +7,13 @@ interface Props {
export const UploadSuccess = ({ title, onOk }: Props) => {
return (
<div {...{ className: "obj-uploader-modal-success-view" }}>
<div
{...{
className: "uploader-message",
}}>{`Added ${title} to objects`}</div>
<>
<div className="w-100 text-center opacity-50">{`Added ${title} to your library`}</div>
<div className="mt-6 flex justify-end gap-2">
<Button
{...{
onClick: onOk,
variant: "primary",
}}>
<Button onClick={onOk} variant="primary">
Ok
</Button>
</div>
</div>
</>
);
};
@@ -1,3 +0,0 @@
export const getFileName = (file: File) => {
return file.name.split(".")[0] || "";
};
+14
View File
@@ -0,0 +1,14 @@
export enum MediaFileAnimationType {
ArKit = "ar_kit",
MikuMikuDance = "miku_miku_dance",
MikuMikuDanceArKit = "miku_miku_dance_ar_kit",
Mixamo = "mixamo",
MixamoArKit = "mixamo_ar_kit",
MocapNet = "mocap_net",
MocapNetArKit = "mocap_net_ar_kit",
MoveAi = "move_ai",
MoveAiArKit = "move_ai_ar_kit",
Rigify = "rigify",
RigifyArKit = "rigify_ar_kit",
Rokoko = "rokoko",
}
+1
View File
@@ -7,6 +7,7 @@ export * from "./ClipGroup";
export * from "./ClipType";
export * from "./FileTypes";
export * from "./JobStatus";
export * from "./MediaFileAnimationTypes";
export * from "./QueryFilters";
export * from "./toasts";
export * from "./Visibility";
@@ -8,18 +8,17 @@ import {
AssetFilterOption,
AssetType,
FilterEngineCategories,
MediaFileAnimationType,
ToastTypes,
} from "~/enums";
import { animationFilter, animationItems } from "~/pages/PageEnigma/signals";
import { addToast } from "~/signals";
import { MediaFileAnimationType } from "~/api/media_files/UploadNewEngineAsset";
import { MediaFileEngineCategory } from "~/api/media_files/UploadEngineAsset";
import { ItemElements } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ItemElements";
import { Button, UploadModalMovement } from "~/components";
import { TabTitle } from "~/pages/PageEnigma/comps/SidePanelTabs/comps/TabTitle";
import { MediaFilesApi } from "~/Classes/ApiManager";
export const AnimationTab = () => {
@@ -127,7 +126,6 @@ export const AnimationTab = () => {
/>
</div>
<UploadModalMovement
closeModal={() => setOpen(false)}
onClose={() => setOpen(false)}
onSuccess={refetchAnimations}
isOpen={open}
@@ -140,7 +138,7 @@ export const AnimationTab = () => {
{ Rigify: MediaFileAnimationType.Rigify },
{ Rokoko: MediaFileAnimationType.Rokoko },
]}
type={MediaFileEngineCategory.Animation}
type={FilterEngineCategories.ANIMATION}
/>
</>
);
@@ -8,11 +8,10 @@ import {
AssetType,
FilterEngineCategories,
ToastTypes,
MediaFileAnimationType,
} from "~/enums";
import { expressionFilter } from "~/pages/PageEnigma/signals";
import { MediaFileEngineCategory } from "~/api/media_files/UploadEngineAsset";
import { MediaFileAnimationType } from "~/api/media_files/UploadNewEngineAsset";
import { ItemElements } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ItemElements";
import {
@@ -170,7 +169,7 @@ export const ExpressionTab = () => {
fileTypes={["CSV"]}
title="Upload Expression"
typeOptions={[{ ARKit: MediaFileAnimationType.ArKit }]}
type={MediaFileEngineCategory.Expression}
type={FilterEngineCategories.EXPRESSION}
/>
</>
);
+4 -1
View File
@@ -31,7 +31,10 @@ export const isJobStatusTerminal = (curr: JobStatus) => {
};
export const getFileName = (file: File) => {
return file.name.split(".")[0] || "";
return file.name.substring(0, file.name.lastIndexOf("."));
};
export const getFileExtension = (file: File) => {
return file.name.substring(file.name.lastIndexOf("."));
};
export const getFileTypesFromEnum = (enumOfTypes: object) => {