implement v2v with the new audio upload in Api Manager, and big cleanup (#515)

- simplify the upload component in v2v because the api manager made it
easier now
- comb through all the old api files
- since nothing refers to them anymore, they are deleted

Co-authored-by: wil <wil@storyteller.ai>
This commit is contained in:
wilandwil authored and GitHub committed 2024-06-20 22:54:37 -04:00
1 parent 2630baeeb6
commit 865f009ee2
14 files changed
+52 -409

No files matched your search

-34
View File
@@ -1,34 +0,0 @@
// interface ApiDetails { hostname: string, useSsl: boolean, }
const domainWithoutPort = document.location.host.split(":")[0];
const hostConfig = (host: string, useSsl = true) => ({
formatUrl: (endpoint = "") => `${ useSsl ? "https" : "http" }://${ host + endpoint }`,
host,
useSsl,
});
export default function GetApiHost() {
switch (domainWithoutPort) {
case "fakeyou.com":
case "staging.fakeyou.com":
return hostConfig("api.fakeyou.com");
case "storyteller.ai":
case "staging.storyteller.ai":
//return hostConfig("api.storyteller.ai");
return hostConfig("api.fakeyou.com");
case "storyteller.stream": // Storyteller.stream is deprecated and will be decommissioned in the future.
return hostConfig("api.storyteller.stream");
case "devproxy.fakeyou.com":
return hostConfig("api.fakeyou.com");
case "devproxy.storyteller.ai":
//return hostConfig("api.storyteller.ai");
return hostConfig("api.fakeyou.com");
case "dev.fakeyou.com":
return hostConfig("api.dev.fakeyou.com:12345",false); // false disables SSL
default:
return document.location.host.includes("localhost") ?
hostConfig("localhost:12345",document.location.protocol === "https:") :
hostConfig("api.fakeyou.com"); // default
}
};
-29
View File
@@ -1,29 +0,0 @@
const MakeMultipartRequest = (endpoint = "", body: any) => {
const formData = new FormData();
Object.keys(body).forEach((key) => formData.append(key, body[key]));
formData.append("source", "file");
return fetch(`https://api.storyteller.ai${endpoint}`, {
method: "POST",
credentials: "include",
headers: {
Accept: "application/json",
},
body: formData,
})
.then((res) => res.json())
.then((res) => {
if (res && res.success) {
return res;
} else {
return { success: false };
}
})
.catch((e) => {
return { success: false };
});
};
export default MakeMultipartRequest;
-66
View File
@@ -1,66 +0,0 @@
import { environmentVariables } from "~/signals";
type UrlRoutingFunction<UrlRouteArgs> = (urlRouteArgs: UrlRouteArgs) => string;
interface RouteSetup<UrlRouteArgs> {
method: string;
multipart?: boolean;
routingFunction: UrlRoutingFunction<UrlRouteArgs>;
}
const METHOD_OMITS_BODY: { [key: string]: boolean } = {
DELETE: false,
GET: true,
OPTIONS: true,
PATCH: false,
POST: false,
PUT: false,
};
const MakeRequest = <UrlRouteArgs, Request, Response, UrlParams>(
routeSetup: RouteSetup<UrlRouteArgs>,
): ((
urlRouteArgs: UrlRouteArgs,
request: Request,
queries?: UrlParams,
) => Promise<Response>) => {
return async function (
urlRouteArgs: UrlRouteArgs,
request: Request,
queries?: any,
): Promise<Response> {
const newQueries = queries
? Object.keys(queries)
.map((key, i) => {
return `${i ? "&" : ""}${key}=${Array.isArray(queries[key]) ? queries[key].join(`&${key}=`) : queries[key]}`;
})
.join("")
: null;
const endpoint = `${routeSetup.routingFunction(urlRouteArgs)}${newQueries ? "?" + newQueries : ""}`;
const method = routeSetup.method;
const methodOmitsBody = METHOD_OMITS_BODY[method] || false;
// return fetch(formatUrl(endpoint), {
return fetch(`${environmentVariables.value.BASE_API}${endpoint}`, {
method,
headers: {
Accept: "application/json",
...(methodOmitsBody ? {} : { "Content-Type": "application/json" }),
},
credentials: "include",
...(methodOmitsBody ? {} : { body: JSON.stringify(request) }),
})
.then((res) => res.json())
.then((res) => {
if (!res.success && res && (res.voice_token || res.dataset_token)) {
return res;
} else if (res && "success" in res) {
return res;
} else Promise.reject();
})
.catch(() => ({ success: false }));
};
};
export default MakeRequest;
-26
View File
@@ -1,26 +0,0 @@
import MakeRequest from "../MakeRequest";
// import { Weight } from "../weights/GetWeight";
export interface UserBookmarksListRequest {}
export interface UserBookmarksListResponse {
// pagination: Pagination,
success: boolean;
// results: Weight[];
}
export interface UserBookmarksListQueries {
page_index?: number;
page_size: number;
}
export const GetBookmarksByUser = MakeRequest<
string,
UserBookmarksListRequest,
UserBookmarksListResponse,
UserBookmarksListQueries
>({
method: "GET",
routingFunction: (userToken: string) =>
`/v1/user_bookmarks/list/user/${userToken}`,
});
+1 -1
View File
@@ -19,7 +19,7 @@ export const apiHost = "https://api.storyteller.ai";
//V2V
// export const listV2V = `${apiHost}/v1/voice_conversion/model_list`;
// export const inferV2V = `${apiHost}/v1/voice_conversion/inference`;
export const uploadAudioV2V = `${apiHost}/v1/media_uploads/upload_audio`;
// export const uploadAudioV2V = `${apiHost}/v1/media_uploads/upload_audio`;
// User Media
// export const listMediaByUser = (username: string) =>
-14
View File
@@ -1,14 +0,0 @@
import MakeRequest from "../MakeRequest";
export interface EditCoverImageRequest {
cover_image_media_file_token: string
}
export interface EditCoverImageResponse {
success: boolean
}
export const EditCoverImage = MakeRequest<string, EditCoverImageRequest, EditCoverImageResponse,{}>({
method: "POST",
routingFunction: (media_token: string) => `/v1/media_files/cover_image/${ media_token }`,
});
-69
View File
@@ -1,69 +0,0 @@
import MakeMultipartRequest from "../MakeMultipartRequest";
export enum MediaFileEngineCategory {
Scene = "scene",
Character = "character",
Animation = "animation",
Object = "object",
Skybox = "skybox",
Expression = "expression",
}
export enum MediaFileAnimationType {
ar_kit = "ar_kit",
miku_miku_dance = "miku_miku_dance",
miku_miku_dance_ar_kit = "miku_miku_dance_ar_kit",
mixamo = "mixamo",
mixamo_ar_kit = "mixamo_ar_kit",
mocap_net = "mocap_net",
mocap_net_ar_kit = "mocap_net_ar_kit",
move_ai = "move_ai",
move_ai_ar_kit = "move_ai_ar_kit",
rigify = "rigify",
rigify_ar_kit = "rigify_ar_kit",
}
export enum MediaFileSubtype {
/// Animation file from Mixamo
/// Primarily used for FBX and GLB.
Mixamo = "mixamo",
/// Animation file from MocapNet
/// Primarily used for BVH.
MocapNet = "mocap_net",
/// Generic animation case
/// Used for BVH files, but can also pertain to animation-only files of other types.
AnimationOnly = "animation_only",
// TODO(bt,2024-03-08): Migrate records and code, then remove
/// DEPRECATED: Use `SceneImport` instead.
Scene = "scene",
/// Generic 3D scene file.
/// Can pertain to glTF, glB, FBX, etc.
SceneImport = "scene_import",
/// Native Storyteller scene format.
/// Typically stored in a `.scn.ron` file.
StorytellerScene = "storyteller_scene",
}
export interface UploadEngineAssetRequest {
engine_category: MediaFileEngineCategory;
file: File;
maybe_animation_type: MediaFileAnimationType | null;
maybe_title?: string;
maybe_visibility?: string;
uuid_idempotency_token?: string;
}
export interface UploadEngineAssetResponse {
media_file_token: string;
engine_category: string;
success: boolean;
}
export const UploadEngineAsset = (request: UploadEngineAssetRequest) => {
return MakeMultipartRequest("/v1/media_files/upload/engine_asset", request);
};
-18
View File
@@ -1,18 +0,0 @@
import MakeMultipartRequest from "../MakeMultipartRequest";
export interface UploadMediaRequest {
uuid_idempotency_token?: string;
file: File;
source: string;
title?: string;
// dataset_token: string,
}
export interface UploadMediaResponse {
media_file_token: string;
success: boolean;
}
export const UploadMedia = (request: UploadMediaRequest) => {
return MakeMultipartRequest("/v1/media_files/upload", request);
};
@@ -1,47 +0,0 @@
import MakeMultipartRequest from "../MakeMultipartRequest";
export enum MediaFileEngineCategory {
Scene = "scene",
Character = "character",
Animation = "animation",
Expression = "expression",
Object = "object",
Skybox = "skybox",
}
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",
}
export interface UploadNewEngineAssetRequest {
engine_category: MediaFileEngineCategory;
file: File;
maybe_animation_type?: MediaFileAnimationType;
maybe_title?: string;
maybe_visibility?: "public" | "private";
maybe_duration_millis?: number;
uuid_idempotency_token?: string;
}
export interface UploadNewEngineAssetResponse {
media_file_token: string;
success: boolean;
}
export const UploadNewEngineAsset = (request: UploadNewEngineAssetRequest) => {
return MakeMultipartRequest(
"/v1/media_files/upload/new_engine_asset",
request,
);
};
+1 -1
View File
@@ -2,7 +2,7 @@ import { Fragment } from "react";
import { Listbox, Transition } from "@headlessui/react";
import { faCheck, faChevronDown } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { MediaFileAnimationType } from "~/api/media_files/UploadNewEngineAsset";
import { MediaFileAnimationType } from "~/enums";
export type SelectValue = string | number;
export type SelectOption = { label: string; value: SelectValue };
@@ -86,7 +86,6 @@ export const UploadAudioButtonDialogue = ({
}
//handle errors
console.log(response.errorMessage);
addToast(
ToastTypes.ERROR,
response.errorMessage || "Unknown Error: Upload Audio",
@@ -1,4 +1,4 @@
import { useState, useEffect } from "react";
import { useCallback, useState } from "react";
import { FileUploader } from "react-drag-drop-files";
import { v4 as uuidv4 } from "uuid";
import {
@@ -11,32 +11,30 @@ import {
} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { UploadAudio, UploadAudioIsOk, UploadAudioRequest } from "./utilities";
import { Button, ButtonIcon, P, Tooltip, WaveformPlayer } from "~/components";
import { AUDIO_FILE_TYPE } from "~/enums";
import { MediaUploadApi } from "~/Classes/ApiManager";
import { getFileName } from "~/utilities";
import { AUDIO_FILE_TYPE, ToastTypes } from "~/enums";
import { addToast } from "~/signals";
const FILE_TYPES = Object.keys(AUDIO_FILE_TYPE);
interface Props {
file?: File;
onFileStaged?: (file: File) => void;
onClear?: () => void;
onFileUploaded: (token: string) => void;
}
export const UploadAudioComponent = ({
file: propsFile,
onFileStaged,
onClear,
onFileUploaded,
}: Props) => {
const [{ file, uploadState, uploadToken }, setState] = useState<{
const [{ file, uploadState }, setState] = useState<{
file: File | undefined;
uploadState: "init" | "none" | "uploading" | "uploaded" | "error";
uploadToken?: string;
uploadState: "none" | "staged" | "uploading" | "uploaded" | "error";
}>({
file: propsFile,
uploadState: propsFile ? "none" : "init",
file: undefined,
uploadState: "none",
});
const audioUrl = file ? URL.createObjectURL(file) : "";
@@ -45,8 +43,11 @@ export const UploadAudioComponent = ({
setState((curr) => ({
...curr,
file: file,
uploadState: "none",
uploadState: "staged",
}));
if (onFileStaged) {
onFileStaged(file);
}
};
const handleClear = () => {
@@ -56,42 +57,43 @@ export const UploadAudioComponent = ({
uploadState: "none",
uploadToken: undefined,
}));
if (onClear) {
onClear();
}
};
const handleUploadFile = () => {
if (file === undefined) return false;
const handleUploadFile = useCallback(
async (file: File) => {
setState((curr) => ({ ...curr, uploadState: "uploading" }));
setState((curr) => ({ ...curr, uploadState: "uploading" }));
const request: UploadAudioRequest = {
uuid_idempotency_token: uuidv4(),
file: file,
source: "file",
};
UploadAudio(request).then((res) => {
if (UploadAudioIsOk(res)) {
const mediaUploadApi = new MediaUploadApi();
const request = {
blob: file,
fileName: getFileName(file),
uuid: uuidv4(),
maybe_title: getFileName(file),
};
const response = await mediaUploadApi.UploadAudio(request);
if (response.success && response.data) {
setState((curr) => ({
...curr,
uploadState: "uploaded",
uploadToken: res.upload_token,
}));
} else {
setState((curr) => ({
...curr,
uploadState: "error",
uploadToken: undefined,
}));
onFileUploaded(response.data);
return;
}
});
};
useEffect(() => {
if (file && uploadState === "none" && onFileStaged) onFileStaged(file);
if (!file && uploadState === "none" && onClear) onClear();
}, [file, uploadState]);
useEffect(() => {
if (uploadState === "uploaded" && uploadToken) onFileUploaded(uploadToken);
}, [uploadToken, uploadState]);
//handle errors
addToast(
ToastTypes.ERROR,
response.errorMessage || "Unknown Error: Upload Audio",
);
setState((curr) => ({
...curr,
uploadState: "staged",
}));
},
[onFileUploaded],
);
return (
<div className="flex flex-col gap-3">
@@ -123,8 +125,14 @@ export const UploadAudioComponent = ({
<Button
className="w-full py-2.5"
variant="action"
onClick={handleUploadFile}
disabled={uploadState !== "none" && uploadState !== "error"}
onClick={() => {
if (!file) {
//TODO: Maybe more verbose on not having file
return false;
}
handleUploadFile(file);
}}
disabled={uploadState !== "staged"}
icon={
uploadState === "uploaded"
? faCheck
@@ -1,47 +0,0 @@
import { uploadAudioV2V } from "~/api";
export interface UploadAudioRequest {
uuid_idempotency_token: string;
source?: string; // eg. "device", "file"
file: File;
}
export interface UploadAudioResponse {
success: boolean;
upload_token?: string;
}
export function UploadAudioIsOk(response: UploadAudioResponse) {
return response?.success === true;
}
export async function UploadAudio(
request: UploadAudioRequest,
): Promise<UploadAudioResponse> {
const formData = new FormData();
formData.append("uuid_idempotency_token", request.uuid_idempotency_token);
formData.append("file", request.file);
if (request.source !== undefined) {
formData.append("source", request.source);
}
return fetch(uploadAudioV2V, {
method: "POST",
headers: {
Accept: "application/json",
},
credentials: "include",
body: formData,
})
.then((res) => res.json())
.then((res) => {
if (res && "success" in res) {
return res;
}
return { success: false };
})
.catch(() => {
return { success: false };
});
}
@@ -92,20 +92,6 @@ export const PageVoicetoVoice = ({
}}
/>
</div>
{/* <div className="my-4 flex w-full gap-4 justify-between items-center">
<span className="bg-brand-secondary grow h-1"/>
<H4>OR</H4>
<span className="bg-brand-secondary grow h-1"/>
</div> */}
{/* <Button
className="h-11 w-full text-sm"
variant="secondary"
icon={faCircleDot}
onClick={() => {
console.log("Record Button is clicked");
}}>
Record Audio
</Button> */}
<Button
className="w-full py-3 text-sm"
variant="primary"