Compare commits

..
Author SHA1 Message Date
bflatastic 727246b90f added z index to scene state 2025-07-21 14:48:03 +07:00
35 changed files with 72 additions and 370 deletions
@@ -1,12 +0,0 @@
{
"db_name": "MySQL",
"query": "\nUPDATE generic_inference_jobs\nSET\n status = 'cancelled_by_system',\n retry_at = NULL,\n internal_debugging_failure_reason = 'reap_stale_fakeyou_jobs'\nWHERE\n job_type != 'fal_queue'\n AND status IN (\n 'pending',\n 'started',\n 'attempt_failed'\n )\n AND created_at > NOW() - INTERVAL 30 MINUTE\nLIMIT 5000\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 0
},
"nullable": []
},
"hash": "19708db99f2b3c6283d2115a511e0eb1c99fab396ad0effd9eb2dd79926e9c93"
}
@@ -21,8 +21,6 @@ CREATE TABLE generic_inference_jobs (
-- The new enum for the type of job, which will eventually replace `inference_category` and `maybe_model_type`.
-- This isn't fully supported yet in the inference-job, but we'll start populating it and adding logic around it.
-- We'll start this out as nullable, then migrate the old rows with a default column value.
--
-- Fal jobs have the type `fal_queue`.
job_type VARCHAR(32) DEFAULT NULL,
-- There is an index on this column.
@@ -1,43 +0,0 @@
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,7 +2,6 @@ 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;
-2
View File
@@ -12,7 +12,6 @@ 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;
@@ -155,7 +154,6 @@ 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,
@@ -1,4 +1,3 @@
use log::{debug, warn};
use crate::payloads::generic_inference_args::inner_payloads::f5_tts_payload::F5TTSPayload;
use crate::payloads::generic_inference_args::inner_payloads::face_fusion_payload::FaceFusionPayload;
use crate::payloads::generic_inference_args::inner_payloads::gptsovits_payload::GptSovitsPayload;
@@ -181,19 +180,6 @@ impl GenericInferenceArgs {
Ok(serde_json::from_str(json)?)
}
pub fn from_json_optional(json: Option<&str>) -> AnyhowResult<Option<Self>> {
match json {
None => Ok(None),
Some("" | "null" | "NULL" | "undefined" | "UNDEFINED") => {
// NB: Something inserted a few of these records (only in local dev).
// It's better not to stall the job queue than to assert correctness.
warn!("Received empty or null JSON `{json:?}` for GenericInferenceArgs, returning None.");
Ok(None)
},
Some(json_str) => Ok(Some(Self::from_json(json_str)?)),
}
}
pub fn to_json(&self) -> AnyhowResult<String> {
Ok(serde_json::to_string(self)?)
}
@@ -2,7 +2,6 @@ use std::collections::BTreeSet;
use anyhow::anyhow;
use chrono::Utc;
use log::debug;
use sqlx::MySqlPool;
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
@@ -152,8 +151,10 @@ pub async fn list_available_generic_inference_jobs(
.map(|token_type| InferenceInputSourceTokenType::from_str(token_type)
.map_err(|err| anyhow!("error: {:?}", err)))
.transpose()?,
maybe_inference_args: GenericInferenceArgs::from_json_optional(
record.maybe_inference_args.as_deref())?,
maybe_inference_args: record.maybe_inference_args
.as_deref()
.map(|args| GenericInferenceArgs::from_json(args))
.transpose()?,
maybe_raw_inference_text: record.maybe_raw_inference_text,
status: JobStatusPlus::from_str(&record.status)
.map_err(|err| anyhow!("JobStatus failure to parse: {:?}", err))?,
@@ -5,4 +5,3 @@ pub mod mark_generic_inference_job_completely_failed;
pub mod mark_generic_inference_job_failure;
pub mod mark_generic_inference_job_pending_and_grab_lock;
pub mod mark_generic_inference_job_successfully_done;
pub mod reap_stale_fakeyou_jobs;
@@ -1,52 +0,0 @@
use anyhow::anyhow;
use log::info;
use sqlx;
use sqlx::pool::PoolConnection;
use sqlx::{MySql, MySqlPool};
use errors::AnyhowResult;
/// Kill FakeYou jobs that are "stale".
///
/// - This leaves Fal jobs alone, as they do not use the job runner.
/// - This kills jobs that are over a threshold (e.g. an hour) overdue.
pub async fn reap_stale_fakeyou_jobs(mysql_pool: &MySqlPool) -> AnyhowResult<()>
{
let mut connection = mysql_pool.acquire().await?;
reap_stale_fakeyou_jobs_using_connection(&mut connection).await
}
pub async fn reap_stale_fakeyou_jobs_using_connection(
mysql_connection: &mut PoolConnection<MySql>,
) -> AnyhowResult<()> {
let query_result = sqlx::query!(
r#"
UPDATE generic_inference_jobs
SET
status = 'cancelled_by_system',
retry_at = NULL,
internal_debugging_failure_reason = 'reap_stale_fakeyou_jobs'
WHERE
job_type != 'fal_queue'
AND status IN (
'pending',
'started',
'attempt_failed'
)
AND created_at > NOW() - INTERVAL 30 MINUTE
LIMIT 5000
"#
)
.execute(&mut **mysql_connection)
.await;
match query_result {
Ok(result) => {
info!("Reaped stale FakeYou jobs: {} rows affected.", result.rows_affected());
Ok(())
},
Err(err) => {
Err(anyhow!("error with reaping stale jobs: {:?}", err))
}
}
}
@@ -40,8 +40,6 @@ pub fn run_http_server(args: CreateServerArgs) -> AnyhowResult<Server>
// NB: We shouldn't be logging much as the /_status endpoint is all we aim to expose.
let log_format = "[%{HOSTNAME}e] IP=[%{X-Forwarded-For}i] \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\" %T";
info!("HTTP server will bind to: {}", bind_address);
let handle = HttpServer::new(move || {
// NB: app_data being clone()'d below should all be safe (dependencies included)
App::new()
@@ -53,7 +53,6 @@ use crate::state::job_dependencies::{BucketDependencies, ClientDependencies, Dat
use crate::state::job_specific_dependencies::JobSpecificDependencies;
use crate::state::scoped_job_type_execution::ScopedJobTypeExecution;
use crate::state::scoped_model_type_execution::ScopedModelTypeExecution;
use crate::threads::reap_jobs_thread::reap_jobs_thread;
use crate::util::filesystem::scoped_temp_dir_creator::ScopedTempDirCreator;
use crate::util::instrumentation::JobInstruments;
use crate::util::instrumentation::{init_otel_metrics_pipeline, JobInstrumentLabels};
@@ -62,7 +61,6 @@ use crate::util::model_weights_cache::model_weights_cache_directory::ModelWeight
pub mod http_server;
pub mod job;
pub mod state;
pub mod threads;
pub mod util;
// Buckets (shared config)
@@ -165,8 +163,6 @@ async fn main() -> AnyhowResult<()> {
.connect(&db_connection_string)
.await?;
let mysql_pool_2 = mysql_pool.clone();
info!("Connected to MySQL.");
let common_env = CommonEnv::read_from_env()?;
@@ -349,14 +345,6 @@ async fn main() -> AnyhowResult<()> {
set_up_directories(&job_dependencies)?;
std::thread::spawn(move || {
let actix_runtime = actix_web::rt::System::new();
actix_runtime.block_on(reap_jobs_thread(mysql_pool_2));
warn!("Reap jobs thread terminated");
});
std::thread::spawn(move || {
let actix_runtime = actix_web::rt::System::new();
let http_server_handle = launch_http_server(create_server_args);
@@ -1 +0,0 @@
pub mod reap_jobs_thread;
@@ -1,33 +0,0 @@
use errors::AnyhowResult;
use log::{error, info};
use mysql_queries::queries::generic_inference::job::reap_stale_fakeyou_jobs::reap_stale_fakeyou_jobs;
use sqlx::MySqlPool;
use std::time::Duration;
const WAIT_BETWEEN_REAPING_SECONDS: u64 = 60 * 2; // 2 minutes
pub async fn reap_jobs_thread(
mysql_pool: MySqlPool,
) -> ! {
info!("Database job reaping thread starting...");
loop {
tokio::time::sleep(Duration::from_secs(WAIT_BETWEEN_REAPING_SECONDS)).await;
info!("Reaping jobs from database...");
let result = do_reap_jobs(&mysql_pool).await;
if let Err(err) = result {
error!("Error reaping jobs: {:?}", err);
tokio::time::sleep(Duration::from_secs(300)).await;
}
}
}
pub async fn do_reap_jobs(
mysql_pool: &MySqlPool,
) -> AnyhowResult<()> {
reap_stale_fakeyou_jobs(mysql_pool).await?;
Ok(())
}
@@ -1 +0,0 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Flux</title><path d="M0 20.683L12.01 2.5 24 20.683h-2.233L12.009 5.878 3.471 18.806h12.122l1.239 1.877H0z"></path><path d="M8.069 16.724l2.073-3.115 2.074 3.115H8.069zM18.24 20.683l-5.668-8.707h2.177l5.686 8.707h-2.196zM19.74 11.676l2.13-3.19 2.13 3.19h-4.26z"></path></svg>

Before

Width:  |  Height:  |  Size: 438 B

@@ -1 +0,0 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>ByteDance</title><path d="M14.944 18.587l-1.704-.445V10.01l1.824-.462c1-.254 1.84-.461 1.88-.453.032 0 .056 2.235.056 4.972v4.973l-.176-.008c-.104 0-.952-.207-1.88-.446z"></path><path d="M7 16.542c0-2.736.024-4.98.064-4.98.032-.008.872.2 1.88.454l1.816.461-.016 4.05-.024 4.049-1.632.422c-.896.23-1.736.445-1.856.469L7 21.523v-4.98z"></path><path d="M19.24 12.477c0-9.03.008-9.515.144-9.475.072.024.784.207 1.576.406.792.207 1.576.405 1.744.445l.296.08-.016 8.56-.024 8.568-1.624.414c-.888.23-1.728.437-1.856.47l-.24.055v-9.523z"></path><path d="M1 12.509c0-4.678.024-8.505.064-8.505.032 0 .872.207 1.872.454l1.824.461v7.582c0 4.16-.016 7.574-.032 7.574-.024 0-.872.215-1.88.47L1 21.013v-8.505z"></path></svg>

Before

Width:  |  Height:  |  Size: 873 B

@@ -1 +0,0 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Gemini</title><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z"></path></svg>

Before

Width:  |  Height:  |  Size: 651 B

@@ -1 +0,0 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Kling</title><path clip-rule="evenodd" d="M5.493 21.234c-1.112-1.451-1.109-4.263-.081-7.459l-4.557-2.63a1.683 1.683 0 01-.85-1.304 1.505 1.505 0 01.08-.622 13.18 13.18 0 011.037-2.255c3.476-6.02 10.916-8.23 16.619-4.938.46.266.82.67 1.081 1.184.785 1.545.685 4.096-.234 6.954l4.557 2.631c.339.196.596.492.736.832a1.53 1.53 0 01.034 1.093 13.146 13.146 0 01-1.037 2.255c-3.476 6.02-10.916 8.23-16.619 4.938a2.6 2.6 0 01-.766-.68zm11.096-6.615c-2.073 3.591-5.808 5.316-8.343 3.852-1.267-.731-1.994-2.122-2.145-3.778-.095-1.035.036-2.173.4-3.32.217-.684.517-1.37.902-2.039l.008-.014c2.073-3.59 5.808-5.315 8.343-3.852.633.366 1.13.895 1.49 1.54.986 1.772.922 4.415-.285 6.914-.111.23-.232.457-.362.683l-.008.014z"></path></svg>

Before

Width:  |  Height:  |  Size: 888 B

@@ -1 +0,0 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Midjourney</title><path d="M22.369 17.676c-1.387 1.259-3.17 2.378-5.332 3.417.044.03.086.057.13.083l.018.01.019.012c.216.123.42.184.641.184.222 0 .426-.061.642-.184l.018-.011.019-.011c.14-.084.266-.178.492-.366l.178-.148c.279-.232.426-.342.625-.456.304-.174.612-.266.949-.266.337 0 .645.092.949.266l.023.014c.188.109.334.219.602.442l.178.148c.221.184.346.278.483.36l.028.017.018.01c.21.12.407.181.62.185h.022a.31.31 0 110 .618c-.337 0-.645-.092-.95-.266a3.137 3.137 0 01-.09-.054l-.022-.014-.022-.013-.02-.014a5.356 5.356 0 01-.49-.377l-.159-.132a3.836 3.836 0 00-.483-.36l-.027-.017-.019-.01a1.256 1.256 0 00-.641-.185c-.222 0-.426.061-.641.184l-.02.011-.018.011c-.14.084-.266.178-.492.366l-.158.132a5.125 5.125 0 01-.51.39l-.022.014-.022.014-.09.054a1.868 1.868 0 01-.95.266c-.337 0-.644-.092-.949-.266a3.137 3.137 0 01-.09-.054l-.022-.014-.022-.013-.026-.017a4.881 4.881 0 01-.425-.325.308.308 0 01-.12-.1l-.098-.081a3.836 3.836 0 00-.483-.36l-.027-.017-.019-.01a1.256 1.256 0 00-.641-.185c-.222 0-.426.061-.642.184l-.018.011-.019.011c-.14.084-.266.178-.492.366l-.158.132a5.125 5.125 0 01-.51.39l-.023.014-.022.014-.09.054A1.868 1.868 0 0112 22c-.337 0-.645-.092-.949-.266a3.137 3.137 0 01-.09-.054l-.022-.014-.022-.013-.021-.014a5.356 5.356 0 01-.49-.377l-.158-.132a3.836 3.836 0 00-.483-.36l-.028-.017-.018-.01a1.256 1.256 0 00-.642-.185c-.221 0-.425.061-.641.184l-.019.011-.018.011c-.141.084-.266.178-.492.366l-.158.132a5.125 5.125 0 01-.511.39l-.022.014-.022.014-.09.054a1.868 1.868 0 01-.986.264c-.746-.09-1.319-.38-1.89-.866l-.035-.03c-.047-.041-.118-.106-.192-.174l-.196-.181-.107-.1-.011-.01a1.531 1.531 0 00-.336-.253.313.313 0 00-.095-.03h-.005c-.119.022-.238.059-.361.11a.308.308 0 01-.077.061l-.008.005a.309.309 0 01-.126.034 5.66 5.66 0 00-.774.518l-.416.324-.055.043a6.542 6.542 0 01-.324.236c-.305.207-.552.315-.8.315a.31.31 0 01-.01-.618h.01c.09 0 .235-.062.438-.198l.04-.027c.077-.054.163-.117.27-.199l.385-.301.06-.047c.268-.206.506-.373.73-.505l-.633-1.21a.309.309 0 01.254-.451l20.287-1.305a.309.309 0 01.228.537zm-1.118.14L2.369 19.03l.423.809c.128-.045.256-.078.388-.1a.31.31 0 01.052-.005c.132 0 .26.032.386.093.153.073.294.179.483.35l.016.015.092.086.144.134.097.089c.065.06.125.114.16.144.485.418.948.658 1.554.736h.011a1.25 1.25 0 00.6-.172l.021-.011.019-.011.018-.011c.141-.084.266-.178.492-.366l.178-.148c.279-.232.426-.342.625-.456.305-.174.612-.266.95-.266.336 0 .644.092.948.266l.023.014c.188.109.335.219.603.442l.177.148c.222.184.346.278.484.36l.027.017.019.01c.215.124.42.185.641.185.222 0 .426-.061.641-.184l.019-.011.018-.011c.141-.084.267-.178.493-.366l.177-.148c.28-.232.427-.342.626-.456.304-.174.612-.266.949-.266.337 0 .644.092.949.266l.025.015c.187.109.334.22.603.443 1.867-.878 3.448-1.811 4.73-2.832l.02-.016zM3.653 2.026C6.073 3.06 8.69 4.941 10.8 7.258c2.46 2.7 4.109 5.828 4.637 9.149a.31.31 0 01-.421.335c-2.348-.945-4.54-1.258-6.59-1.02-1.739.2-3.337.792-4.816 1.703-.294.182-.62-.182-.405-.454 1.856-2.355 2.581-4.99 2.343-7.794-.195-2.292-1.031-4.61-2.284-6.709a.31.31 0 01.388-.442zM10.04 4.45c1.778.543 3.892 2.102 5.782 4.243 1.984 2.248 3.552 4.934 4.347 7.582a.31.31 0 01-.401.38l-.022-.01-.386-.154a10.594 10.594 0 00-.291-.112l-.016-.006c-.68-.247-1.199-.291-1.944-.101a.31.31 0 01-.375-.218C15.378 11.123 13.073 7.276 9.775 5c-.291-.201-.072-.653.266-.55zM4.273 2.996l.008.015c1.028 1.94 1.708 4.031 1.885 6.113.213 2.513-.31 4.906-1.673 7.092l-.02.031.003-.001c1.198-.581 2.47-.969 3.825-1.132l.055-.006c1.981-.23 4.083.029 6.309.837l.066.025-.007-.039c-.593-2.95-2.108-5.737-4.31-8.179l-.07-.078c-1.785-1.96-3.944-3.6-6.014-4.65l-.057-.028zm7.92 3.238l.048.048c2.237 2.295 3.885 5.431 4.974 9.191l.038.132.022-.004c.71-.133 1.284-.063 1.963.18l.027.01.066.024.046.018-.025-.073c-.811-2.307-2.208-4.62-3.936-6.594l-.058-.065c-1.02-1.155-2.103-2.132-3.15-2.856l-.015-.011z"></path></svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

@@ -1 +0,0 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>OpenAI</title><path d="M21.55 10.004a5.416 5.416 0 00-.478-4.501c-1.217-2.09-3.662-3.166-6.05-2.66A5.59 5.59 0 0010.831 1C8.39.995 6.224 2.546 5.473 4.838A5.553 5.553 0 001.76 7.496a5.487 5.487 0 00.691 6.5 5.416 5.416 0 00.477 4.502c1.217 2.09 3.662 3.165 6.05 2.66A5.586 5.586 0 0013.168 23c2.443.006 4.61-1.546 5.361-3.84a5.553 5.553 0 003.715-2.66 5.488 5.488 0 00-.693-6.497v.001zm-8.381 11.558a4.199 4.199 0 01-2.675-.954c.034-.018.093-.05.132-.074l4.44-2.53a.71.71 0 00.364-.623v-6.176l1.877 1.069c.02.01.033.029.036.05v5.115c-.003 2.274-1.87 4.118-4.174 4.123zM4.192 17.78a4.059 4.059 0 01-.498-2.763c.032.02.09.055.131.078l4.44 2.53c.225.13.504.13.73 0l5.42-3.088v2.138a.068.068 0 01-.027.057L9.9 19.288c-1.999 1.136-4.552.46-5.707-1.51h-.001zM3.023 8.216A4.15 4.15 0 015.198 6.41l-.002.151v5.06a.711.711 0 00.364.624l5.42 3.087-1.876 1.07a.067.067 0 01-.063.005l-4.489-2.559c-1.995-1.14-2.679-3.658-1.53-5.63h.001zm15.417 3.54l-5.42-3.088L14.896 7.6a.067.067 0 01.063-.006l4.489 2.557c1.998 1.14 2.683 3.662 1.529 5.633a4.163 4.163 0 01-2.174 1.807V12.38a.71.71 0 00-.363-.623zm1.867-2.773a6.04 6.04 0 00-.132-.078l-4.44-2.53a.731.731 0 00-.729 0l-5.42 3.088V7.325a.068.068 0 01.027-.057L14.1 4.713c2-1.137 4.555-.46 5.707 1.513.487.833.664 1.809.499 2.757h.001zm-11.741 3.81l-1.877-1.068a.065.065 0 01-.036-.051V6.559c.001-2.277 1.873-4.122 4.181-4.12.976 0 1.92.338 2.671.954-.034.018-.092.05-.131.073l-4.44 2.53a.71.71 0 00-.365.623l-.003 6.173v.002zm1.02-2.168L12 9.25l2.414 1.375v2.75L12 14.75l-2.415-1.375v-2.75z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -15,7 +15,6 @@ import { setCanvasRenderBitmap } from "../../signals/canvasRenderBitmap";
import { captureStageImageBitmap } from "./hooks/useUpdateSnapshot";
import { ContextMenuContainer } from "./components/ui/ContextMenu";
import { FalBackgroundRemoval } from "@storyteller/tauri-api";
import { getCreatorIcon, ModelCreator } from "@storyteller/model-list";
export const DecodeBase64ToImage = async (
base64String: string,
@@ -147,18 +146,14 @@ const PageDraw = () => {
const modelList: PopoverItem[] = [
{
label: "GPT-4o",
icon: getCreatorIcon(ModelCreator.OpenAi) || (
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
selected: selectedModel === "GPT-4o",
description: "High quality model",
badges: [{ label: "2 min.", icon: <FontAwesomeIcon icon={faClock} /> }],
},
{
label: "FLUX.1 Kontext",
icon: getCreatorIcon(ModelCreator.BlackForestLabs) || (
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
selected: selectedModel === "FLUX.1 Kontext",
description: "Fast and high-quality model",
badges: [{ label: "20 sec.", icon: <FontAwesomeIcon icon={faClock} /> }],
@@ -11,12 +11,13 @@ import { ChromaKeyMaterial } from "./chromakey";
import { TimeLine } from "./timeline";
import { ClipGroup, ClipType } from "~/enums";
import { ClipUI } from "../clips/clip_ui";
import { LoadWithoutCors } from "@storyteller/tauri-api";
import { GetFrontendEnvironment } from "~/Classes/GetFrontendEnvironment";
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;
@@ -624,38 +625,19 @@ class Scene {
// await this.delay(3000); // artificial delay.
// 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;
// });
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}`);
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) {
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);
}
toast.error("Failed to load 3D asset.");
}).catch((error: Error) => {
throw error;
});
@@ -753,41 +735,6 @@ 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
@@ -24,8 +24,7 @@ const TextToImage = ({ imageMediaId, imageUrl }: TextToImageProps) => {
selectedModels[ModelCategory.TextToImage] ||
imageGenerationModels[0]?.label;
const selectedModelInfo: ModelInfo | undefined =
IMAGE_MODELS_BY_LABEL[selectedModel];
const selectedModelInfo : ModelInfo | undefined = IMAGE_MODELS_BY_LABEL[selectedModel];
const jobContext: JobContextType = {
jobTokens: [],
@@ -24,8 +24,7 @@ const ImageToVideo = ({ imageMediaId, imageUrl }: ImageToVideoProps) => {
selectedModels[ModelCategory.ImageToVideo] ||
videoGenerationModels[0]?.label;
const selectedModelInfo: ModelInfo | undefined =
VIDEO_MODELS_BY_LABEL[selectedModel];
const selectedModelInfo : ModelInfo | undefined = VIDEO_MODELS_BY_LABEL[selectedModel];
const jobContext: JobContextType = {
jobTokens: [],
@@ -1,3 +1,7 @@
/**
* Defines the unique categories for each product or context where a model
* selection is required. Using an enum prevents typos and ensures type safety.
*/
export enum ModelCategory {
TextToImage = "text-to-image",
ImageToVideo = "image-to-video",
@@ -1,12 +1,7 @@
import type { PopoverItem } from "@storyteller/ui-popover";
import { faClock, faFilm, faImage } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
IMAGE_MODELS,
VIDEO_MODELS,
getCreatorIcon,
ModelCreator,
} from "@storyteller/model-list";
import { IMAGE_MODELS } from "@storyteller/model-list";
export type ModelList = Omit<PopoverItem, "selected">[];
@@ -14,92 +9,67 @@ export const allModels = {
video: [
{
label: "Kling 1.6 Pro",
icon: getCreatorIcon(ModelCreator.Kling) || (
<FontAwesomeIcon icon={faFilm} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faFilm} className="h-4 w-4" />,
description: "Good quality model",
badges: [{ label: "2 min.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: VIDEO_MODELS.kling_1_6_pro,
},
{
label: "Kling 2.1 Pro",
icon: getCreatorIcon(ModelCreator.Kling) || (
<FontAwesomeIcon icon={faFilm} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faFilm} className="h-4 w-4" />,
description: "High quality model",
badges: [{ label: "2 min.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: VIDEO_MODELS.kling_2_1_pro,
},
{
label: "Kling 2.1 Master",
icon: getCreatorIcon(ModelCreator.Kling) || (
<FontAwesomeIcon icon={faFilm} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faFilm} className="h-4 w-4" />,
description: "Master quality model ($$)",
badges: [{ label: "2 min.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: VIDEO_MODELS.kling_2_1_master,
},
{
label: "Seedance 1.0 Lite",
icon: getCreatorIcon(ModelCreator.Bytedance) || (
<FontAwesomeIcon icon={faFilm} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faFilm} className="h-4 w-4" />,
description: "Fast and high-quality model",
badges: [{ label: "2 min.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: VIDEO_MODELS.seedance_1_0_lite,
},
{
label: "Google Veo 2",
icon: getCreatorIcon(ModelCreator.Google) || (
<FontAwesomeIcon icon={faFilm} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faFilm} className="h-4 w-4" />,
description: "Fast and high-quality model",
badges: [{ label: "2 min.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: VIDEO_MODELS.veo_2,
},
],
image: [
{
label: "Flux Pro 1.1 Ultra",
icon: getCreatorIcon(ModelCreator.BlackForestLabs) || (
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
description: "High quality model",
badges: [{ label: "15 sec.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: IMAGE_MODELS.flux_pro_1_1_ultra,
},
{
label: "Flux Pro 1.1",
icon: getCreatorIcon(ModelCreator.BlackForestLabs) || (
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
description: "High quality model",
badges: [{ label: "15 sec.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: IMAGE_MODELS.flux_pro_1_1,
},
{
label: "Flux 1 Dev",
icon: getCreatorIcon(ModelCreator.BlackForestLabs) || (
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
description: "High quality model",
badges: [{ label: "15 sec.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: IMAGE_MODELS.flux_1_dev,
},
{
label: "Flux 1 Schnell",
icon: getCreatorIcon(ModelCreator.BlackForestLabs) || (
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
description: "High quality model",
badges: [{ label: "15 sec.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: IMAGE_MODELS.flux_1_schnell,
},
{
label: "GPT Image 1 (GPT-4o)",
icon: getCreatorIcon(ModelCreator.OpenAi) || (
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
description: "Slow, ultra instructive model",
badges: [{ label: "45 sec.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: IMAGE_MODELS.gpt_image_1,
@@ -123,18 +93,14 @@ export const allModels = {
instructiveImageEdits: [
{
label: "GPT-4o",
icon: getCreatorIcon(ModelCreator.OpenAi) || (
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
description: "High quality model",
badges: [{ label: "2 min.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: IMAGE_MODELS.gpt_image_1,
},
{
label: "FLUX.1 Kontext",
icon: getCreatorIcon(ModelCreator.BlackForestLabs) || (
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
),
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
description: "Fast and high-quality model",
badges: [{ label: "20 sec.", icon: <FontAwesomeIcon icon={faClock} /> }],
modelInfo: IMAGE_MODELS.gpt_image_1, // TODO: Wrong metadata!
@@ -144,5 +110,4 @@ export const allModels = {
export const videoGenerationModels: ModelList = allModels.video;
export const imageGenerationModels: ModelList = allModels.image;
export const instructiveImageEditModels: ModelList =
allModels.instructiveImageEdits;
export const instructiveImageEditModels: ModelList = allModels.instructiveImageEdits;
@@ -8,6 +8,12 @@
{
"path": "../camera-settings-modal"
},
{
"path": "../../soundboard"
},
{
"path": "../action-reminder-modal"
},
{
"path": "../../tauri-api"
},
@@ -49,6 +49,12 @@
{
"path": "../camera-settings-modal/tsconfig.lib.json"
},
{
"path": "../../soundboard/tsconfig.lib.json"
},
{
"path": "../action-reminder-modal/tsconfig.lib.json"
},
{
"path": "../../tauri-api/tsconfig.lib.json"
},
+6 -6
View File
@@ -1,6 +1,6 @@
export * from "./lib/model-list.js";
export * from "./lib/ModelInfo.js";
export * from "./lib/ModelCreator.js";
export * from "./lib/ImageModels.js";
export * from "./lib/VideoModels.js";
export * from "./lib/ModelCreatorIcons.js";
export * from './lib/model-list.js';
export * from './lib/ModelInfo.js';
export * from './lib/ModelCreator.js';
export * from './lib/ImageModels.js';
export * from './lib/VideoModels.js';
@@ -26,10 +26,10 @@ export const IMAGE_MODELS: Record<string, ModelInfo> = {
name: "GPT Image 1 (GPT-4o)",
tauri_id: "gpt_image_1",
creator: ModelCreator.OpenAi,
},
};
}
}
// This is a hack to get the old model selector code to work since it's keyed off
// This is a hack to get the old model selector code to work since it's keyed off
// of the human-readable label of the model. Long term, that should be fixed.
export const IMAGE_MODELS_BY_LABEL: Record<string, ModelInfo> = {
"Flux Pro 1.1 Ultra": IMAGE_MODELS.flux_pro_1_1_ultra,
@@ -37,4 +37,5 @@ export const IMAGE_MODELS_BY_LABEL: Record<string, ModelInfo> = {
"Flux 1 Dev": IMAGE_MODELS.flux_1_dev,
"Flux 1 Schnell": IMAGE_MODELS.flux_1_schnell,
"GPT Image 1 (GPT-4o)": IMAGE_MODELS.gpt_image_1,
};
}
@@ -1,28 +0,0 @@
import React, { ReactNode } from "react";
import { ModelCreator } from "./ModelCreator.js";
export const CREATOR_ICON_PATHS: Partial<Record<ModelCreator, string>> = {
[ModelCreator.BlackForestLabs]:
"/resources/images/services/blackforestlabs.svg",
[ModelCreator.Kling]: "/resources/images/services/kling.svg",
[ModelCreator.Midjourney]: "/resources/images/services/midjourney.svg",
[ModelCreator.OpenAi]: "/resources/images/services/openai.svg",
[ModelCreator.Bytedance]: "/resources/images/services/bytedance.svg",
[ModelCreator.Google]: "/resources/images/services/google.svg",
};
export const getCreatorIconPath = (creator: ModelCreator): string | undefined =>
CREATOR_ICON_PATHS[creator];
export const getCreatorIcon = (
creator: ModelCreator,
className = "h-4 w-4 invert"
): ReactNode | null => {
const path = getCreatorIconPath(creator);
if (!path) return null;
return React.createElement("img", {
src: path,
alt: `${creator} logo`,
className,
});
};
@@ -3,11 +3,11 @@ import { ModelCreator } from "./ModelCreator.js";
export interface ModelInfo {
// Human-readible name
// Other components may want to rename the model, eg. to fit in smaller width elements
name: string;
name: string,
// The identifier that Tauri uses (it may send different signals downstream)
tauri_id: string;
tauri_id: string,
// Creator of the model type
creator: ModelCreator;
creator: ModelCreator,
}
@@ -26,10 +26,10 @@ export const VIDEO_MODELS: Record<string, ModelInfo> = {
name: "Google Veo 2",
tauri_id: "veo_2",
creator: ModelCreator.Google,
},
};
}
}
// This is a hack to get the old model selector code to work since it's keyed off
// This is a hack to get the old model selector code to work since it's keyed off
// of the human-readable label of the model. Long term, that should be fixed.
export const VIDEO_MODELS_BY_LABEL: Record<string, ModelInfo> = {
"Kling 1.6 Pro": VIDEO_MODELS.kling_1_6_pro,
@@ -37,4 +37,5 @@ export const VIDEO_MODELS_BY_LABEL: Record<string, ModelInfo> = {
"Kling 2.1 Master": VIDEO_MODELS.kling_2_1_master,
"Seedance 1.0 Lite": VIDEO_MODELS.seedance_1_0_lite,
"Google Veo 2": VIDEO_MODELS.veo_2,
};
}
@@ -1,3 +1,3 @@
export function modelList(): string {
return "model-list";
return 'model-list';
}
+1 -1
View File
@@ -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";
@@ -1,13 +0,0 @@
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;
}
}