mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 02:00:46 +00:00
add unit-tested way to construct Engine URLs
This commit is contained in:
1 parent
be2efc8b44
commit
a803bfb79b
3 files changed
+211
No files matched your search
@@ -0,0 +1,5 @@
|
||||
|
||||
export enum EngineMode {
|
||||
Viewer = "viewer",
|
||||
Studio = "studio",
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import { MediaFileType } from "@storyteller/components/src/api/_common/enums/MediaFileType";
|
||||
import { EngineMode } from "./EngineMode";
|
||||
import { GetEngineUrl } from "./GetEngineUrl";
|
||||
import { WeightCategory } from "@storyteller/components/src/api/_common/enums/WeightCategory";
|
||||
import { WeightType } from "@storyteller/components/src/api/_common/enums/WeightType";
|
||||
import { MediaFileSubtype } from "@storyteller/components/src/api/enums/MediaFileSubtype";
|
||||
|
||||
describe('mode', () => {
|
||||
test('studio', () => {
|
||||
const url = GetEngineUrl({mode: EngineMode.Studio, load: { objectId: "foo" } });
|
||||
expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&objectId=foo");
|
||||
});
|
||||
|
||||
test('viewer', () => {
|
||||
const url = GetEngineUrl({mode: EngineMode.Viewer, load: { objectId: "foo" } });
|
||||
expect(url).toEqual("https://engine.fakeyou.com/?mode=viewer&objectId=foo");
|
||||
});
|
||||
});
|
||||
|
||||
describe('skybox', () => {
|
||||
test('from named skybox', () => {
|
||||
const url = GetEngineUrl({mode: EngineMode.Studio, load: { objectId: "foo" }, skybox: "gum_trees_4k" });
|
||||
expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&skybox=gum_trees_4k&objectId=foo");
|
||||
});
|
||||
|
||||
test('from hex color', () => {
|
||||
const url = GetEngineUrl({mode: EngineMode.Studio, load: { objectId: "foo" }, skybox: "ff0000" });
|
||||
expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&skybox=ff0000&objectId=foo");
|
||||
});
|
||||
});
|
||||
|
||||
describe('object ids', () => {
|
||||
test('object id urls should work', () => {
|
||||
const url = GetEngineUrl({mode: EngineMode.Studio, load: { objectId: "foo" } });
|
||||
expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&objectId=foo");
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(bt,2024-03-11): We're only testing one type of media file
|
||||
describe('media files', () => {
|
||||
let mediaFile = {
|
||||
token: "MEDIA_FILE_TOKEN",
|
||||
media_type: MediaFileType.GLB,
|
||||
maybe_media_subtype: null,
|
||||
public_bucket_path: "path/to/file",
|
||||
maybe_batch_token: "BATCH_TOKEN",
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
maybe_creator_user: null,
|
||||
creator_set_visibility: "public",
|
||||
// TODO(bt,2024-03-11): Make optional
|
||||
maybe_model_weight_info: {
|
||||
title: "title",
|
||||
weight_token: "WEIGHT_TOKEN",
|
||||
weight_category: WeightCategory.SD,
|
||||
weight_type: WeightType.HIFIGAN_TT2,
|
||||
maybe_cover_image_public_bucket_path: "image",
|
||||
maybe_weight_creator: {
|
||||
user_token: "USER_TOKEN",
|
||||
username: "username",
|
||||
display_name: "display_name",
|
||||
gravatar_hash: "foo",
|
||||
default_avatar: {
|
||||
image_index: 1,
|
||||
color_index: 2,
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
describe('scene files', () => {
|
||||
test('from media type', () => {
|
||||
mediaFile.media_type = MediaFileType.SceneRon;
|
||||
|
||||
// NB: Not the real subtype; forcing test to act on type.
|
||||
mediaFile.maybe_media_subtype = null;
|
||||
|
||||
const url = GetEngineUrl({mode: EngineMode.Studio, load: mediaFile });
|
||||
expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&scene=remote://MEDIA_FILE_TOKEN.scn.ron");
|
||||
});
|
||||
|
||||
test('from media subtype', () => {
|
||||
// NB: Not the real time; forcing test to act on subtype.
|
||||
mediaFile.media_type = MediaFileType.Audio;
|
||||
|
||||
// TODO(bt,2024-03-11): Why does the IDE complain here?
|
||||
mediaFile.maybe_media_subtype = MediaFileSubtype.StorytellerScene as any;
|
||||
|
||||
const url = GetEngineUrl({mode: EngineMode.Studio, load: mediaFile });
|
||||
expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&scene=remote://MEDIA_FILE_TOKEN.scn.ron");
|
||||
});
|
||||
});
|
||||
|
||||
describe('mixamo animations', () => {
|
||||
test('from media subtype', () => {
|
||||
// NB: Not the real time; forcing test to act on subtype.
|
||||
mediaFile.media_type = MediaFileType.Audio;
|
||||
|
||||
// TODO(bt,2024-03-11): Why does the IDE complain here?
|
||||
mediaFile.maybe_media_subtype = MediaFileSubtype.Mixamo as any;
|
||||
|
||||
const url = GetEngineUrl({mode: EngineMode.Studio, load: mediaFile });
|
||||
expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&mixamo=https://storage.googleapis.com/dev-vocodes-public/path/to/file");
|
||||
});
|
||||
});
|
||||
|
||||
describe('bvh animations', () => {
|
||||
test('from media subtype', () => {
|
||||
// NB: Not the real time; forcing test to act on subtype.
|
||||
mediaFile.media_type = MediaFileType.Audio;
|
||||
|
||||
// TODO(bt,2024-03-11): Why does the IDE complain here?
|
||||
mediaFile.maybe_media_subtype = MediaFileSubtype.MocapNet as any;
|
||||
|
||||
const url = GetEngineUrl({mode: EngineMode.Studio, load: mediaFile });
|
||||
expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&bvh=https://storage.googleapis.com/dev-vocodes-public/path/to/file");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
import { MediaFile as MediaFileOne } from "@storyteller/components/src/api/media_files/GetMedia";
|
||||
import { MediaFile as MediaFileTwo } from "@storyteller/components/src/api/media_files/GetMediaFile";
|
||||
import { EngineMode } from "./EngineMode";
|
||||
import { MediaFileSubtype } from "@storyteller/components/src/api/enums/MediaFileSubtype";
|
||||
import { MediaFileType } from "@storyteller/components/src/api/_common/enums/MediaFileType";
|
||||
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
|
||||
|
||||
// Storyteller Engine parameters
|
||||
// These are documented here:
|
||||
// https://www.notion.so/storytellerai/Studio-Iframe-Query-Params-a748a9929ec3404780c3884e7fb89bdb
|
||||
|
||||
interface GetEngineUrlArgs {
|
||||
mode: EngineMode,
|
||||
|
||||
// Polymorphic type of asset we're loading into the engine.
|
||||
load: LoadableAsset
|
||||
|
||||
// Optional skybox color (hex) or name
|
||||
// - Predefined skyboxes, eg. `gum_trees_4k`, `kloofendal_28d_misty_4k`, `meadow_4k`, `promenade_de_vidy_4k`, `scythian_tombs_4k`
|
||||
// - Hex colors, eg. `ff0000` or `1a1a27`.
|
||||
skybox?: string,
|
||||
}
|
||||
|
||||
// Support multiple "Media File" API response payloads.
|
||||
type MediaFile = MediaFileOne | MediaFileTwo ;
|
||||
|
||||
// Used to load raw built-in objects into the engine.
|
||||
interface EngineObject {
|
||||
objectId: string;
|
||||
}
|
||||
|
||||
type LoadableAsset = MediaFile | EngineObject;
|
||||
|
||||
const ENGINE_BASE_URL = "https://engine.fakeyou.com";
|
||||
|
||||
export function GetEngineUrl(args: GetEngineUrlArgs) : string {
|
||||
let engineUrl = `${ENGINE_BASE_URL}/?mode=${args.mode}`;
|
||||
|
||||
|
||||
if (args.skybox) {
|
||||
engineUrl += `&skybox=${args.skybox}`;
|
||||
}
|
||||
|
||||
if (isMediaFile(args.load)) {
|
||||
|
||||
const bucketAssetUrl = new BucketConfig().getGcsUrl(args.load.public_bucket_path);
|
||||
|
||||
if (
|
||||
args.load.maybe_media_subtype === MediaFileSubtype.StorytellerScene ||
|
||||
args.load.media_type === MediaFileType.SceneRon
|
||||
) {
|
||||
// Storyteller Engine Scenes
|
||||
// NB: Storyteller Engine makes the API call to load the scene.
|
||||
// We don't need to pass the bucket path.
|
||||
// The engine, does, however, need a `.scn.ron` file extension.
|
||||
const sceneUrlRef = `remote://${args.load.token}.scn.ron`;
|
||||
engineUrl += `&scene=${sceneUrlRef}`;
|
||||
} else if (
|
||||
args.load.maybe_media_subtype === MediaFileSubtype.Mixamo
|
||||
) {
|
||||
engineUrl += `&mixamo=${bucketAssetUrl}`;
|
||||
} else if (
|
||||
args.load.maybe_media_subtype === MediaFileSubtype.MocapNet
|
||||
) {
|
||||
engineUrl += `&bvh=${bucketAssetUrl}`;
|
||||
} else {
|
||||
engineUrl += `&sceneImport=${bucketAssetUrl}`;
|
||||
}
|
||||
|
||||
} else if (isEngineObject(args.load)) {
|
||||
// NB: This is an engine built-in, eg. `couch.gltf` or `sample-room.gltf`.
|
||||
engineUrl += `&objectId=${args.load.objectId}`;
|
||||
}
|
||||
|
||||
return engineUrl;
|
||||
}
|
||||
|
||||
// Type guard for media files
|
||||
const isMediaFile = (loadable: LoadableAsset) : loadable is MediaFile => {
|
||||
return "token" in loadable;
|
||||
}
|
||||
|
||||
// Type guard for engine objects
|
||||
const isEngineObject = (loadable: LoadableAsset) : loadable is EngineObject => {
|
||||
return "objectId" in loadable;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user