Saving some api manager work and beginning to serialize the timeline

This commit is contained in:
Michael Chung committed 2024-03-26 00:03:05 -04:00
1 parent 2d00e0055a
commit dcc8bd7194
4 files changed
+126 -106

No files matched your search

+99 -64
View File
@@ -1,107 +1,142 @@
import { v4 as uuidv4 } from 'uuid'
import { Scene } from '../datastructures/scene/scene_object'
import * as THREE from 'three'
import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { TimelineCurrentReactState } from './timeline'
import { v4 as uuidv4 } from "uuid";
import { Scene } from "../datastructures/scene/scene_object";
import * as THREE from "three";
import { GLTFExporter } from "three/addons/exporters/GLTFExporter.js";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { TimelineCurrentReactState } from "./timeline";
// Basically the data will exist on the database
// Basically the data will exist on the database
class APIManager {
baseUrl:String
baseUrl: String;
constructor() {
this.baseUrl = "https://api.fakeyou.com"
this.baseUrl = "https://api.fakeyou.com";
//this.baseUrl = "http://localhost:12345"
}
// Function to save or update the scene
async saveSceneState(scene:THREE.Scene,
timeLine:TimelineCurrentReactState,
scene_id:string | null =null):Promise<string> {
if (scene_id == null) {
// it will always return a boolean success and the media_file_token
// they take an optional media_file_token as a parameter for the file to overwrite, which must belong to the user.
//If it's an anonymous user, we check the anonymous visitor token/cookie
async saveSceneState(
scene: THREE.Scene,
timeline: TimelineCurrentReactState,
media_file_token: string | null = null,
): Promise<string> {
if (media_file_token == null) {
// create new scene.
let file = await this.gltfExport(scene)
let response = await this.uploadGLB(file)
console.log(response)
let file = await this.gltfExport(scene);
let response = await this.uploadGLB(file);
let success = response["success"];
if (success) {
let media_file_token = response["media_file_token"];
await this._saveSceneAndTimelineToJSONSpec(media_file_token, timeline);
} else {
throw Error("Saving the Scene Resulted in an Error Success False");
}
} else {
// update scene or overwrite glb scene.
}
return ""
return "";
}
async _saveSceneAndTimelineToJSONSpec() {
// which will create or update/replace scene descriptor JSON files
async _saveSceneAndTimelineToJSONSpec(
media_file_token: string,
timeline: TimelineCurrentReactState,
): Promise<string> {
const url = `${this.baseUrl}/v1/media_files/write/scene_file`;
let uuid = uuidv4();
return "";
}
async loadScene(scene_token:[]):Promise<string> {
return ""
async loadScene(media_file_token: string): Promise<string> {
let json_response = `{
}`
return ""
}
// Provides bucket path to the actual file to download from the media file
async getMediaFile(media_file_token: string): Promise<string> {
let api_base_url = "https://api.fakeyou.com";
let url = `${api_base_url}/v1/media_files/file/${media_file_token}`
let response = await fetch(url);
let json = await JSON.parse(await response.text());
let bucketPath = json["media_file"]["public_bucket_path"];
let media_base_url = "https://storage.googleapis.com/vocodes-public"
let media_url = `${media_base_url}${bucketPath}` // gets you a bucket path
// TODO get it as binary.
return media_url
}
async batchGetMedia(media_tokens: []): Promise<string> {
return "";
}
async batchMedia(media_tokens:[]):Promise<string> {
return ""
}
async gltfExport(scene:THREE.Scene) {
let gltfExporter = new GLTFExporter()
let uuid = uuidv4()
// save the scene to keep ids positions etc
let result = await gltfExporter.parseAsync(scene)
const file = new File([JSON.stringify(result)], `${uuid}.glb`, {type: 'application/json'})
return file
async gltfExport(scene: THREE.Scene) {
let gltfExporter = new GLTFExporter();
let uuid = uuidv4();
// save the scene to keep ids positions etc
let result = await gltfExporter.parseAsync(scene);
const file = new File([JSON.stringify(result)], `${uuid}.glb`, {
type: "application/json",
});
return file;
}
// two core functions to upload things to the server and get id's
async uploadMedia(blob:any, fileName:string):Promise<string> {
const url = `${this.baseUrl}/v1/media_uploads/upload`
let uuid = uuidv4()
const formData = new FormData()
formData.append('uuid_idempotency_token', uuid)
formData.append('file', blob, fileName)
formData.append('source', 'file')
formData.append('type', 'video')
formData.append('source', 'file')
async uploadMedia(blob: any, fileName: string): Promise<string> {
const url = `${this.baseUrl}/v1/media_uploads/upload`;
let uuid = uuidv4();
const formData = new FormData();
formData.append("uuid_idempotency_token", uuid);
formData.append("file", blob, fileName);
formData.append("source", "file");
formData.append("type", "video");
formData.append("source", "file");
const response = await fetch(url, {
method: 'POST',
method: "POST",
credentials: "include",
headers: {
"Accept": "application/json",
Accept: "application/json",
},
body: formData,
})
});
if (!response.ok) {
throw new Error('Failed to Send Data')
throw new Error("Failed to Send Data");
}
return response.json() // or handle the response as appropriate
return response.json(); // or handle the response as appropriate
}
// to save to a file then upload the scene.
async uploadGLB(file:File):Promise<string> {
const url = `${this.baseUrl}/v1/media_files/upload/engine_asset`
let uuid = uuidv4()
const formData = new FormData()
formData.append('uuid_idempotency_token', uuid)
formData.append('file', file)
formData.append('source','file')
formData.append('media_file_subtype', 'scene_import')
formData.append('media_file_class', 'scene')
async uploadGLB(file: File): Promise<string> {
// /v1/media_files/write/engine_asset REPLACE with this api endpoint
const url = `${this.baseUrl}/v1/media_files/upload/engine_asset`;
let uuid = uuidv4();
const formData = new FormData();
formData.append("uuid_idempotency_token", uuid);
formData.append("file", file);
formData.append("source", "file");
formData.append("media_file_subtype", "scene_import");
formData.append("media_file_class", "scene");
const response = await fetch(url, {
method: 'POST',
method: "POST",
credentials: "include",
headers: {
"Accept": "application/json",
Accept: "application/json",
},
body: formData,
})
});
if (!response.ok) {
throw new Error('Failed to Send Data')
throw new Error("Failed to Send Data");
}
return response.json() // or handle the response as appropriate
return response.json(); // or handle the response as appropriate
}
}
export default APIManager
export default APIManager;
-34
View File
@@ -1,34 +0,0 @@
import * as THREE from "three";
class AudioClip {
constructor(listener, audioURL) {
this.audioLoader = new THREE.AudioLoader();
this.sound = new THREE.Audio(listener);
this.audioURL = audioURL;
this.isLoaded = false;
// Load the audio file asynchronously
this.audioLoader.load(audioURL, (buffer) => {
this.sound.setBuffer(buffer);
this.isLoaded = true;
});
}
play() {
if (this.isLoaded) {
this.sound.play();
this.sound.setLoop(true);
this.sound.setVolume(1);
this.sound.play();
} else {
console.warn(`AudioClip: Sound not loaded yet (${this.audioURL}).`);
}
}
stop() {
if (this.isLoaded) {
this.sound.stop();
}
}
}
+21 -6
View File
@@ -165,10 +165,30 @@ class Editor {
// This is the default scene.
// "m_189p8hj0eyypbg74kkhcpehwpjhnkz" scene with the fox.
let result = await this.api_manager.saveSceneState(this.activeScene.scene)
//let result = await this.api_manager.saveSceneState(this.activeScene.scene)
let result = await this.api_manager.getMediaFile("m_189p8hj0eyypbg74kkhcpehwpjhnkz")
console.log(result)
}
async _serialize_timeline() {
// note the database from the server is the source of truth for all the data.
// Test code here
let object = await this.activeScene.load_glb("./resources/models/fox/fox.glb")
// load object into the engine for lip syncing
this.lipsync_engine.load_object(object.uuid, "m_f1jxx4zwy4da2zn0cvdqhha7kqkj72");
// create the clip with the same id for a reference to the media
this.timeline.addPlayableClip(new ClipUI(1.0, "lipsync", "clip1", "m_f1jxx4zwy4da2zn0cvdqhha7kqkj72", object.uuid, 150, 400));
// media id for this is up in the air but when a path is created you should be able to store and delete it
this.timeline.addPlayableClip(new ClipUI(1.0, "transform", "clip2", object.uuid, object.uuid, 0, 150));
// media id for this as well it can be downloaded
this.timeline.addPlayableClip(new ClipUI(1.0, "animation", "clip3", "/resources/models/fox/fox_idle.glb", object.uuid, 0, 400));
this.animation_engine.load_object(object.uuid, "/resources/models/fox/fox_idle.glb", "clip3");
}
async _test_demo() {
// note the database from the server is the source of truth for all the data.
// Test code here
@@ -283,7 +303,6 @@ class Editor {
//this.transform_engine.clips[this.test_box_uuid].reset(this.activeScene.get_object_by_uuid(this.test_box_uuid));
}
// Basicly Unity 3D's update loop.
update_loop(time) {
// Updates debug stats.
@@ -307,10 +326,6 @@ class Editor {
this.timeline.isPlaying = true;
}
// _save_to_cloud(blob) {
// this.api_manager.uploadGLB(blob, "test.glb");
// }
change_mode(type) {
this.control.mode = type;
this.transform_interaction = true;
+6 -2
View File
@@ -9,7 +9,7 @@ import AnimationEngine from "./animation_engine";
// Every object uuid / entity has a track.
export class TimelineCurrentReactState {
isEditable: boolean
isEditable: boolean
selectedObjectID: number
constructor() {
@@ -19,6 +19,8 @@ export class TimelineCurrentReactState {
}
export class TimeLine {
timelineItems: ClipUI[]
runningClips: ClipUI[]
@@ -45,6 +47,7 @@ export class TimeLine {
lipsyncEngine: LipSyncEngine,
animationEngine: AnimationEngine,
scene: Scene) {
this.timelineItems = []
this.timeLineLimit = 60 * 10 // 10 seconds
this.runningClips = []
@@ -67,8 +70,9 @@ export class TimeLine {
// when given a media id item it will create the clip.
// Then the clip will be loaded by the engines, if they come from outside of the loaded scene.
async createClipOffset(media_id: string, type: string): Promise<void> {
async createClipOffset(media_id: string, object_uuid:string, type: string): Promise<void> {
// use engine to load based off media id and type animation | transform |
}
// this will update the state of the clips based off uuid easing?