mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-24 02:29:32 -04:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2199cc5e3d | ||
|
|
4cce6720c6 | ||
|
|
66c773f330 |
@@ -0,0 +1,43 @@
|
||||
use futures::TryFutureExt;
|
||||
use log::{error, info};
|
||||
use reqwest::Client;
|
||||
use tauri::ipc::Response;
|
||||
|
||||
/// Request a URL and load it without worrying about CORS.
|
||||
#[tauri::command]
|
||||
pub async fn load_without_cors_command(url: String) -> Result<Response, String> {
|
||||
|
||||
info!("load_without_cors_command for URL: {}", url);
|
||||
|
||||
// TODO(bt,2025-07-22): Make sure the URL is valid and safe to fetch. Check an allowlist.
|
||||
|
||||
// TODO(bt,2025-07-22): Should we cache clients so we don't need to SSL handshake every time?
|
||||
let client = Client::builder()
|
||||
.gzip(true)
|
||||
.build()
|
||||
.map_err(|e| {
|
||||
error!("Failed to create HTTP client: {:?}", e);
|
||||
format!("Failed to create HTTP client: {:?}", e)
|
||||
})?;
|
||||
|
||||
let response = client.get(url.clone())
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Failed to send HTTP request: {:?}", e);
|
||||
format!("Failed to send HTTP request: {:?}", e)
|
||||
})?;
|
||||
|
||||
if response.status() != 200 {
|
||||
return Err(format!("Failed to fetch URL: {}. Status: {}", url, response.status()));
|
||||
}
|
||||
|
||||
let bytes = response.bytes()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Failed to read response bytes: {:?}", e);
|
||||
format!("Failed to read response bytes: {:?}", e)
|
||||
})?;
|
||||
|
||||
Ok(Response::new(bytes.to_vec()))
|
||||
}
|
||||
@@ -2,6 +2,7 @@ pub mod app_preferences;
|
||||
pub mod enqueue;
|
||||
pub mod flip_image;
|
||||
pub mod get_app_info_command;
|
||||
pub mod load_without_cors_command;
|
||||
pub mod platform_info_command;
|
||||
pub mod providers;
|
||||
pub mod response;
|
||||
@@ -12,6 +12,7 @@ use crate::core::commands::enqueue::object::enqueue_image_to_3d_object_command::
|
||||
use crate::core::commands::enqueue::video::enqueue_image_to_video_command::enqueue_image_to_video_command;
|
||||
use crate::core::commands::flip_image::flip_image;
|
||||
use crate::core::commands::get_app_info_command::get_app_info_command;
|
||||
use crate::core::commands::load_without_cors_command::load_without_cors_command;
|
||||
use crate::core::commands::platform_info_command::platform_info_command;
|
||||
use crate::core::commands::providers::get_provider_order_command::get_provider_order_command;
|
||||
use crate::core::commands::providers::set_provider_order_command::set_provider_order_command;
|
||||
@@ -154,6 +155,7 @@ pub fn run() {
|
||||
get_app_preferences_command,
|
||||
get_fal_api_key_command,
|
||||
get_provider_order_command,
|
||||
load_without_cors_command,
|
||||
open_sora_login_command,
|
||||
platform_info_command,
|
||||
set_fal_api_key_command,
|
||||
|
||||
@@ -11,13 +11,12 @@ import { ChromaKeyMaterial } from "./chromakey";
|
||||
import { TimeLine } from "./timeline";
|
||||
import { ClipGroup, ClipType } from "~/enums";
|
||||
import { ClipUI } from "../clips/clip_ui";
|
||||
|
||||
import { GetFrontendEnvironment } from "~/Classes/GetFrontendEnvironment";
|
||||
|
||||
import { LoadWithoutCors } from "@storyteller/tauri-api";
|
||||
import { gridVisibility } from "../signals/engine";
|
||||
import { InfiniteGridHelper } from "./InfiniteGridHelper";
|
||||
import { cameras, selectedCameraId } from "../signals/camera";
|
||||
import { MediaFilesApi } from "@storyteller/api";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
class Scene {
|
||||
name: string;
|
||||
@@ -625,19 +624,38 @@ class Scene {
|
||||
|
||||
// await this.delay(3000); // artificial delay.
|
||||
|
||||
const glb = await this.load_glb_wrapped(url, async (progress) => {
|
||||
const total_loaded = progress.loaded / progress.total;
|
||||
//const percent = this.floatToPercent(total_loaded);
|
||||
//console.log(`GLB Loading: ${percent}`);
|
||||
|
||||
// NB(bt,2025-07-22): This is causing a lot of CORS errors in dev, so I'm starting
|
||||
// to funnel everything through Tauri. Not sure if that'll be more inefficient due
|
||||
// to spinning up new HTTP clients with new SSL handshakes without caching, but at
|
||||
// least it should bypass the cors errors.
|
||||
//
|
||||
// const glb = await this.load_glb_wrapped(url, async (progress) => {
|
||||
// const total_loaded = progress.loaded / progress.total;
|
||||
// //const percent = this.floatToPercent(total_loaded);
|
||||
// //console.log(`GLB Loading: ${percent}`);
|
||||
//
|
||||
// if (total_loaded == 1.0) {
|
||||
// if (this.placeholder_manager === undefined) {
|
||||
// throw Error("Place holder Manager is undefined");
|
||||
// }
|
||||
// //console.log(`GLB Loading: ${total_loaded} === ${key}`);
|
||||
// await this.placeholder_manager.remove(key);
|
||||
// }
|
||||
// }).catch((error: Error) => {
|
||||
// throw error;
|
||||
// });
|
||||
|
||||
if (total_loaded == 1.0) {
|
||||
if (this.placeholder_manager === undefined) {
|
||||
throw Error("Place holder Manager is undefined");
|
||||
}
|
||||
//console.log(`GLB Loading: ${total_loaded} === ${key}`);
|
||||
const glb = await this.load_glb_wrapped_no_cors(url, async () => {
|
||||
if (this.placeholder_manager === undefined) {
|
||||
throw Error("Place holder Manager is undefined");
|
||||
}
|
||||
await this.placeholder_manager.remove(key);
|
||||
}).catch(async (error) => {
|
||||
if (this.placeholder_manager !== undefined) {
|
||||
await this.placeholder_manager.remove(key);
|
||||
}
|
||||
}).catch((error: Error) => {
|
||||
toast.error("Failed to load 3D asset.");
|
||||
throw error;
|
||||
});
|
||||
|
||||
@@ -735,6 +753,41 @@ class Scene {
|
||||
return child_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a full media_url via Tauri (without browser cors)
|
||||
* @param media_url
|
||||
* @param onComplete
|
||||
* @returns
|
||||
*/
|
||||
private async load_glb_wrapped_no_cors(
|
||||
media_url: string,
|
||||
onComplete: () => void,
|
||||
) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let buffer;
|
||||
try {
|
||||
buffer = await LoadWithoutCors(media_url); // Load via Tauri!
|
||||
} catch (error) {
|
||||
console.error("load GLB from Tauri error:", error)
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
const glbLoader = new GLTFLoader();
|
||||
glbLoader.parse(
|
||||
buffer,
|
||||
"", // NB: No relative path to load additional assets!
|
||||
(gltf) => {
|
||||
resolve(gltf);
|
||||
onComplete();
|
||||
},
|
||||
(error) => {
|
||||
console.error("GLTF loader error:", error)
|
||||
reject(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Using a constructed media_url load a glb, grab the progress.
|
||||
* @param media_url
|
||||
|
||||
@@ -21,4 +21,4 @@ export * from "./lib/system/GetAppInfo.js";
|
||||
export * from "./lib/providers/SetProviderOrder.js";
|
||||
export * from "./lib/providers/Provider.js";
|
||||
export * from "./lib/providers/GetProviderOrder.js";
|
||||
|
||||
export * from "./lib/util/LoadWithoutCors.js";
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
export const LoadWithoutCors = async (url: string) : Promise<ArrayBuffer> => {
|
||||
try {
|
||||
// Tauri Result<Success, Error> - success case.
|
||||
let result = await invoke("load_without_cors_command", { url });
|
||||
return result as ArrayBuffer;
|
||||
} catch (error) {
|
||||
// Tauri Result<Success, Error> - error case.
|
||||
console.error("LoadWithoutCors error", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user