From e046e408b3a29a65608230adca5e123f201c6864 Mon Sep 17 00:00:00 2001 From: Danny McGee Date: Fri, 8 Mar 2024 17:37:07 -0500 Subject: [PATCH] More endpoints --- studio-web/src/lib/storyteller-api.ts | 123 +++++++++++++++++++++++--- 1 file changed, 112 insertions(+), 11 deletions(-) diff --git a/studio-web/src/lib/storyteller-api.ts b/studio-web/src/lib/storyteller-api.ts index 5ec1cde..a56d804 100644 --- a/studio-web/src/lib/storyteller-api.ts +++ b/studio-web/src/lib/storyteller-api.ts @@ -1,3 +1,4 @@ +import { match } from "@storyteller/utility"; import axios, { AxiosResponse } from "axios"; export class StorytellerApi { @@ -7,17 +8,15 @@ export class StorytellerApi { }); async createScene(file: File): Promise { - type Response = { - success: boolean; - media_file_token: MediaFileToken; - } - - const response = await this.#api.postForm("v1/engine/create_scene", { - uuid_idempotency_token: crypto.randomUUID(), - file, - }, { - headers: { Accept: "text/plain" }, - }); + const response = await this.#api.postForm( + "v1/engine/create_scene", + { + uuid_idempotency_token: crypto.randomUUID(), + file, + }, { + headers: { Accept: "text/plain" }, + }, + ); this.#validateResponse(response); return response.data.media_file_token; @@ -52,6 +51,39 @@ export class StorytellerApi { return response.data; } + async uploadMediaFile(file: File, subtype: MediaFileSubtype): Promise { + const [, _basename, extension] = file.name.match(/(.+?)\.(.+)/) ?? []; + const mimeType = match (extension, { + "fbx": () => "application/fbx", + "gltf": () => "model/gltf+json", + "glb": () => "model/gltf-binary", + "scn.ron": () => "text/ron", + _: () => "application/octet-stream", + }); + + const response = await this.#api.postForm( + "v1/media_files/upload/engine_asset", + { + uuid_idempotency_token: crypto.randomUUID(), + file, + media_file_subtype: subtype, + }, + { + headers: { Accept: mimeType }, + }, + ); + this.#validateResponse(response); + + return response.data.media_file_token; + } + + async mediaFile(token: MediaFileToken): Promise { + const response = await this.#api.get(`v1/media_files/file/${token}`); + this.#validateResponse(response); + + return response.data.media_file; + } + #validateResponse(response: R): asserts response is OkResponse { if (response.status !== 200) throw new Error(`API responded with status ${response.status}: "${response.statusText}"`); @@ -91,6 +123,11 @@ export interface ListMediaFilesResult { success: boolean; } +export interface MediaUploadResponse { + success: boolean; + media_file_token: MediaFileToken; +} + /** * Pagination by page id. * @@ -104,6 +141,31 @@ export interface PaginationPage { total_page_count: number; } +export interface MediaFileResult { + success: boolean; + media_file: MediaFile; +} + +export interface MediaFile { + token: MediaFileToken; + media_type: MediaFileType; + maybe_media_subtype?: MediaFileSubtype; + maybe_batch_token?: unknown; + public_bucket_path: string; + maybe_model_weight_info?: unknown; + maybe_creator_user?: UserDetailsLight; + creator_set_visibility: Visibility; + maybe_text_transcript?: string; + maybe_prompt_token?: unknown; + maybe_original_filename?: string; + is_emulated_media_file: boolean; + stats: Stats; + /** Date-time string */ + created_at: string; + /** Date-time string */ + updated_at: string; +} + export interface MediaFileListItem { /** Date-time string */ created_at: string; @@ -115,6 +177,7 @@ export interface MediaFileListItem { /** Text transcripts for TTS, etc. */ maybe_text_transcript?: string; media_type: MediaFileType; + maybe_media_subtype?: MediaFileSubtype; origin: MediaFileOriginDetails; origin_category: MediaFileOriginCategory; origin_product_category: MediaFileOriginProductCategory; @@ -126,6 +189,11 @@ export interface MediaFileListItem { updated_at: string; } +export interface Stats { + positive_rating_count: number; + bookmark_count: number; +} + export type MediaFileForUserListItem = Omit; export enum Visibility { @@ -169,6 +237,39 @@ export enum MediaFileType { ScnRon = "scene_ron", } +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", + /** + * @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", +} + /** Fields useful for enriching media file listings */ export interface MediaFileOriginDetails { maybe_model?: Model;