Compare commits

..
Author SHA1 Message Date
Brandon Thomas f587e19d25 remove gpu loading error 2025-09-27 18:27:33 -04:00
Brandon Thomas cea29135ef codegen db 2025-09-27 18:18:47 -04:00
Brandon Thomas 8f2ebf0e69 Veo 3 / Veo 3 Fast (#856) 2025-09-27 18:18:07 -04:00
Brandon Thomas ddfe83c514 analytics enhancements - track individual sessions (#855) 2025-09-27 17:52:35 -04:00
13 changed files with 166 additions and 222 deletions
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO analytics_app_sessions\nSET\n session_token = ?,\n app_namespace = ?,\n user_token = ?,\n app_version = ?,\n os_platform = ?,\n os_version = ?,\n session_duration_seconds = ?,\n ip_address = ?,\n measurement_count = measurement_count + 1,\n first_active_at = NOW(),\n last_active_at = NOW()\nON DUPLICATE KEY UPDATE\n app_namespace = ?,\n user_token = ?,\n app_version = ?,\n os_platform = ?,\n os_version = ?,\n session_duration_seconds = ?,\n ip_address = ?,\n measurement_count = measurement_count + 1,\n last_active_at = NOW()\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 15
},
"nullable": []
},
"hash": "ff39576a5ec6a23c67f6f61e7eba8392e70b05c42b540bea260a9a4cdc478a5b"
}
@@ -0,0 +1,18 @@
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
use crate::error::storyteller_error::StorytellerError;
use crate::utils::api_host::ApiHost;
use crate::utils::basic_json_post_request::basic_json_post_request;
use artcraft_api_defs::generate::video::generate_veo_3_fast_image_to_video::{GenerateVeo3FastImageToVideoRequest, GenerateVeo3FastImageToVideoResponse, GENERATE_VEO_3_FAST_IMAGE_TO_VIDEO_URL_PATH};
pub async fn generate_veo_3_fast_image_to_video(
api_host: &ApiHost,
maybe_creds: Option<&StorytellerCredentialSet>,
request: GenerateVeo3FastImageToVideoRequest,
) -> Result<GenerateVeo3FastImageToVideoResponse, StorytellerError> {
Ok(basic_json_post_request(
api_host,
GENERATE_VEO_3_FAST_IMAGE_TO_VIDEO_URL_PATH,
maybe_creds,
request,
).await?)
}
@@ -0,0 +1,18 @@
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
use crate::error::storyteller_error::StorytellerError;
use crate::utils::api_host::ApiHost;
use crate::utils::basic_json_post_request::basic_json_post_request;
use artcraft_api_defs::generate::video::generate_veo_3_image_to_video::{GenerateVeo3ImageToVideoRequest, GenerateVeo3ImageToVideoResponse, GENERATE_VEO_3_IMAGE_TO_VIDEO_URL_PATH};
pub async fn generate_veo_3_image_to_video(
api_host: &ApiHost,
maybe_creds: Option<&StorytellerCredentialSet>,
request: GenerateVeo3ImageToVideoRequest,
) -> Result<GenerateVeo3ImageToVideoResponse, StorytellerError> {
Ok(basic_json_post_request(
api_host,
GENERATE_VEO_3_IMAGE_TO_VIDEO_URL_PATH,
maybe_creds,
request,
).await?)
}
@@ -2,4 +2,6 @@ pub mod generate_kling_16_pro_image_to_video;
pub mod generate_kling_21_master_image_to_video;
pub mod generate_kling_21_pro_image_to_video;
pub mod generate_seedance_1_0_lite_image_to_video;
pub mod generate_veo_2_image_to_video;
pub mod generate_veo_2_image_to_video;
pub mod generate_veo_3_fast_image_to_video;
pub mod generate_veo_3_image_to_video;
@@ -25,7 +25,7 @@ pub async fn basic_query_string_post_request<Res: DeserializeOwned>(
// TODO: Please stop using this URL Builder library. It's not very safe or intuitive.
let url = get_route(api_host, route_path, query_params);
info!("Requesting {:?}", &url);
debug!("Requesting {:?}", &url);
let client = Client::builder()
.gzip(true)
@@ -41,6 +41,12 @@ pub enum VideoModel {
#[serde(rename = "veo_2")]
Veo2,
#[serde(rename = "veo_3")]
Veo3,
#[serde(rename = "veo_3_fast")]
Veo3Fast,
}
#[derive(Deserialize)]
@@ -13,6 +13,8 @@ use artcraft_api_defs::generate::video::generate_kling_2_1_master_image_to_video
use artcraft_api_defs::generate::video::generate_kling_2_1_pro_image_to_video::{GenerateKling21ProAspectRatio, GenerateKling21ProImageToVideoRequest};
use artcraft_api_defs::generate::video::generate_seedance_1_0_lite_image_to_video::GenerateSeedance10LiteImageToVideoRequest;
use artcraft_api_defs::generate::video::generate_veo_2_image_to_video::{GenerateVeo2AspectRatio, GenerateVeo2ImageToVideoRequest};
use artcraft_api_defs::generate::video::generate_veo_3_fast_image_to_video::GenerateVeo3FastImageToVideoRequest;
use artcraft_api_defs::generate::video::generate_veo_3_image_to_video::{GenerateVeo3AspectRatio, GenerateVeo3ImageToVideoRequest};
use enums::common::generation_provider::GenerationProvider;
use enums::tauri::tasks::task_type::TaskType;
use fal_client::requests::queue::image_gen::enqueue_flux_pro_11_ultra_text_to_image::{enqueue_flux_pro_11_ultra_text_to_image, FluxPro11UltraTextToImageArgs};
@@ -25,6 +27,8 @@ use storyteller_client::endpoints::generate::video::generate_kling_21_master_ima
use storyteller_client::endpoints::generate::video::generate_kling_21_pro_image_to_video::generate_kling_21_pro_image_to_video;
use storyteller_client::endpoints::generate::video::generate_seedance_1_0_lite_image_to_video::generate_seedance_1_0_lite_image_to_video;
use storyteller_client::endpoints::generate::video::generate_veo_2_image_to_video::generate_veo_2_image_to_video;
use storyteller_client::endpoints::generate::video::generate_veo_3_fast_image_to_video::generate_veo_3_fast_image_to_video;
use storyteller_client::endpoints::generate::video::generate_veo_3_image_to_video::generate_veo_3_image_to_video;
use storyteller_client::utils::api_host::ApiHost;
use tauri::AppHandle;
@@ -202,6 +206,61 @@ pub async fn handle_video_artcraft(
}
}
}
Some(VideoModel::Veo3) => {
info!("enqueue Veo 3 with Artcraft API");
selected_model = Some(GenerationModel::Veo3);
let request = GenerateVeo3ImageToVideoRequest {
uuid_idempotency_token,
media_file_token: request.image_media_token,
aspect_ratio: Some(GenerateVeo3AspectRatio::WideSixteenNine),
prompt: request.prompt,
duration: None,
resolution: None,
generate_audio: None,
};
let result = generate_veo_3_image_to_video(
&app_env_configs.storyteller_host,
Some(&creds),
request,
).await;
match result {
Ok(enqueued) => {
info!("Successfully enqueued Artcraft Veo 3 video generation");
enqueued.inference_job_token
}
Err(err) => {
error!("Failed to use Artcraft Veo 3 video generation: {:?}", err);
return Err(GenerateError::from(err));
}
}
}
Some(VideoModel::Veo3Fast) => {
info!("enqueue Veo 3 Fast with Artcraft API");
selected_model = Some(GenerationModel::Veo3Fast);
let request = GenerateVeo3FastImageToVideoRequest {
uuid_idempotency_token,
media_file_token: request.image_media_token,
prompt: request.prompt,
duration: None,
resolution: None,
generate_audio: None,
};
let result = generate_veo_3_fast_image_to_video(
&app_env_configs.storyteller_host,
Some(&creds),
request,
).await;
match result {
Ok(enqueued) => {
info!("Successfully enqueued Artcraft Veo 3 video generation");
enqueued.inference_job_token
}
Err(err) => {
error!("Failed to use Artcraft Veo 3 video generation: {:?}", err);
return Err(GenerateError::from(err));
}
}
}
};
info!("Successfully enqueued video generation");
@@ -71,6 +71,8 @@ impl TaskEnqueueSuccess{
Some(GenerationModel::Kling21Master) => Some(TaskModelType::Kling21Master),
Some(GenerationModel::Seedance10Lite) => Some(TaskModelType::Seedance10Lite),
Some(GenerationModel::Veo2) => Some(TaskModelType::Veo2),
Some(GenerationModel::Veo3) => Some(TaskModelType::Veo3),
Some(GenerationModel::Veo3Fast) => Some(TaskModelType::Veo3Fast),
Some(GenerationModel::Hunyuan3d2_0) => Some(TaskModelType::Hunyuan3d2_0),
Some(GenerationModel::Hunyuan3d2_1) => Some(TaskModelType::Hunyuan3d2_1),
Some(GenerationModel::Midjourney) => Some(TaskModelType::Midjourney), // NB: This is a generic Midjourney model, version unknown.
@@ -59,6 +59,10 @@ pub enum GenerationModel {
Seedance10Lite,
#[serde(rename = "veo_2")]
Veo2,
#[serde(rename = "veo_3")]
Veo3,
#[serde(rename = "veo_3_fast")]
Veo3Fast,
}
#[derive(Debug, Copy, Clone, Serialize)]
@@ -48,6 +48,10 @@ pub enum TaskModelType {
Seedance10Lite,
#[serde(rename = "veo_2")]
Veo2,
#[serde(rename = "veo_3")]
Veo3,
#[serde(rename = "veo_3_fast")]
Veo3Fast,
// 3D Object generation models
#[serde(rename = "hunyuan_3d_2.0")]
@@ -83,6 +87,8 @@ impl TaskModelType {
Self::Kling21Master => "kling_2.1_master",
Self::Seedance10Lite => "seedance_1.0_lite",
Self::Veo2 => "veo_2",
Self::Veo3 => "veo_3",
Self::Veo3Fast => "veo_3_fast",
// 3D Object generation models
Self::Hunyuan3d2_0 => "hunyuan_3d_2.0",
Self::Hunyuan3d2_1 => "hunyuan_3d_2.1",
@@ -109,6 +115,8 @@ impl TaskModelType {
"kling_2.1_master" => Ok(Self::Kling21Master),
"seedance_1.0_lite" => Ok(Self::Seedance10Lite),
"veo_2" => Ok(Self::Veo2),
"veo_3" => Ok(Self::Veo3),
"veo_3_fast" => Ok(Self::Veo3Fast),
// 3D Object generation models
"hunyuan_3d_2.0" => Ok(Self::Hunyuan3d2_0),
"hunyuan_3d_2.1" => Ok(Self::Hunyuan3d2_1),
@@ -138,6 +146,8 @@ impl TaskModelType {
Self::Kling21Master,
Self::Seedance10Lite,
Self::Veo2,
Self::Veo3,
Self::Veo3Fast,
// 3D Object generation models
Self::Hunyuan3d2_0,
Self::Hunyuan3d2_1,
@@ -173,6 +183,8 @@ mod tests {
assert_serialization(TaskModelType::Kling21Master, "kling_2.1_master");
assert_serialization(TaskModelType::Seedance10Lite, "seedance_1.0_lite");
assert_serialization(TaskModelType::Veo2, "veo_2");
assert_serialization(TaskModelType::Veo3, "veo_3");
assert_serialization(TaskModelType::Veo3Fast, "veo_3_fast");
// 3D Object generation models
assert_serialization(TaskModelType::Hunyuan3d2_0, "hunyuan_3d_2.0");
assert_serialization(TaskModelType::Hunyuan3d2_1, "hunyuan_3d_2.1");
@@ -198,6 +210,8 @@ mod tests {
assert_eq!(TaskModelType::Kling21Master.to_str(), "kling_2.1_master");
assert_eq!(TaskModelType::Seedance10Lite.to_str(), "seedance_1.0_lite");
assert_eq!(TaskModelType::Veo2.to_str(), "veo_2");
assert_eq!(TaskModelType::Veo3.to_str(), "veo_3");
assert_eq!(TaskModelType::Veo3Fast.to_str(), "veo_3_fast");
// 3D Object generation models
assert_eq!(TaskModelType::Hunyuan3d2_0.to_str(), "hunyuan_3d_2.0");
assert_eq!(TaskModelType::Hunyuan3d2_1.to_str(), "hunyuan_3d_2.1");
@@ -223,6 +237,8 @@ mod tests {
assert_eq!(TaskModelType::from_str("kling_2.1_master").unwrap(), TaskModelType::Kling21Master);
assert_eq!(TaskModelType::from_str("seedance_1.0_lite").unwrap(), TaskModelType::Seedance10Lite);
assert_eq!(TaskModelType::from_str("veo_2").unwrap(), TaskModelType::Veo2);
assert_eq!(TaskModelType::from_str("veo_3").unwrap(), TaskModelType::Veo3);
assert_eq!(TaskModelType::from_str("veo_3_fast").unwrap(), TaskModelType::Veo3Fast);
// 3D Object generation models
assert_eq!(TaskModelType::from_str("hunyuan_3d_2.0").unwrap(), TaskModelType::Hunyuan3d2_0);
assert_eq!(TaskModelType::from_str("hunyuan_3d_2.1").unwrap(), TaskModelType::Hunyuan3d2_1);
@@ -231,7 +247,7 @@ mod tests {
#[test]
fn all_variants() {
let mut variants = TaskModelType::all_variants();
assert_eq!(variants.len(), 18);
assert_eq!(variants.len(), 20);
// Image models
assert_eq!(variants.pop_first(), Some(TaskModelType::Flux1Dev));
assert_eq!(variants.pop_first(), Some(TaskModelType::Flux1Schnell));
@@ -250,6 +266,8 @@ mod tests {
assert_eq!(variants.pop_first(), Some(TaskModelType::Kling21Master));
assert_eq!(variants.pop_first(), Some(TaskModelType::Seedance10Lite));
assert_eq!(variants.pop_first(), Some(TaskModelType::Veo2));
assert_eq!(variants.pop_first(), Some(TaskModelType::Veo3));
assert_eq!(variants.pop_first(), Some(TaskModelType::Veo3Fast));
// 3D Object generation models
assert_eq!(variants.pop_first(), Some(TaskModelType::Hunyuan3d2_0));
assert_eq!(variants.pop_first(), Some(TaskModelType::Hunyuan3d2_1));
@@ -11,7 +11,6 @@ import { useSignals } from "@preact/signals-react/runtime";
import { useEffect, useState } from "react";
import * as gpu from "detect-gpu";
import { UsersApi } from "~/Classes/ApiManager";
import { TurnOnGpu } from "~/pages/PageEnigma/TurnOnGpu";
import { PrecisionSelector } from "./comps/PrecisionSelector/PrecisionSelector";
import {
precisionSelectedValue,
@@ -22,7 +21,6 @@ import {
import { InstallSounds } from "~/pages/PageEnigma/InstallSounds";
import { PageEditor } from "~/pages/PageEnigma/PageEditor";
import { GalleryDragComponent } from "@storyteller/ui-gallery-modal";
import { LoadingDots } from "@storyteller/ui-loading";
import {
PricingModal,
CreditsModal,
@@ -105,13 +103,6 @@ export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => {
InstallSounds();
});
if (validGpu === "unknown") {
return <LoadingDots />;
}
if (validGpu === "error") {
return <TurnOnGpu />;
}
const currentReminderModalProps = actionReminderProps.value;
return (
@@ -1,210 +0,0 @@
import * as CopyToClipboard from "react-copy-to-clipboard";
import { useEffect, useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCopy } from "@fortawesome/pro-solid-svg-icons";
export const TurnOnGpu = () => {
const [copied1, setCopied1] = useState(false);
const [copied2, setCopied2] = useState(false);
useEffect(() => {
if (copied1) {
setTimeout(() => setCopied1(false), 2000);
}
}, [copied1]);
useEffect(() => {
if (copied2) {
setTimeout(() => setCopied2(false), 2000);
}
}, [copied2]);
return (
<div className="fixed inset-0 h-screen overflow-y-auto p-16">
<div>
Hardware acceleration is <b>required</b> for StoryTeller Studio to run
properly. To check if this is enabled, please follow the steps for your
browser below.
</div>
<div className="my-4">
<b>Google Chrome: </b>
</div>
<ol>
<li>
<div>
Click the three stacked dots in upper right of the browser window.
</div>
</li>
<li>
<div>
Click the arrow next to <em>&quot;Advanced&quot;</em> and select{" "}
<em>&quot;System.&quot;</em>
</div>
</li>
<li>
<div>
Make sure the box next to{" "}
<em>&quot;Use hardware acceleration when available&quot;</em> is
toggled on.
</div>
</li>
<li>
<div>
Close and restart the browser for the change to take effect.
</div>
</li>
<ul>
<li>
<div>
You can also copy and paste this link{" "}
<CopyToClipboard.CopyToClipboard
text="chrome://settings/system"
onCopy={() => setCopied1(true)}
>
<span className="text-blue">
chrome://settings/system&nbsp;
{copied1 ? (
<span style={{ color: "red" }}>Copied.</span>
) : (
<span className="text-sm">
<FontAwesomeIcon icon={faCopy} />
</span>
)}
&nbsp;
</span>
</CopyToClipboard.CopyToClipboard>
into the address bar. This will bring you right to the system
settings.
</div>
</li>
</ul>
</ol>
<div className="my-4">
<b>Firefox:</b>
</div>
<ol>
<li>
<div>
Click on <em>&quot;Firefox&quot;</em> then{" "}
<em>&quot;Preferences.&quot;</em>
</div>
</li>
<li>
<div>
In the&nbsp;<em>&quot;General&quot;</em>&nbsp;panel, go to the&nbsp;
<em>&quot;Performance&quot; </em>section.
</div>
</li>
<li>
<div>
Uncheck the box next to&nbsp;
<em>&quot;Use recommended performance settings&quot;</em> and make
sure the box next to{" "}
<em>&quot;Use hardware acceleration when available&quot;</em> is
selected.
</div>
</li>
<li>
<div>
Close and restart the browser for the change to take effect.
</div>
</li>
<ul>
<li>
<div>
If you need help, please follow this link:{" "}
<a
href="https://support.mozilla.org/en-US/kb/performance-settings?as=u&amp;utm_source=inproduct"
target="_blank"
rel="noreferrer"
className="text-blue underline"
>
https://support.mozilla.org/en-US/kb/performance-settings?as=u&amp;utm_source=inproduct
</a>
</div>
</li>
</ul>
</ol>
<div className="my-4">
<b>Microsoft Edge:</b>
</div>
<ol>
<li>
<div>
Click the &quot;3 dots&quot; menu icon in the upper right of the
browser window
</div>
</li>
<li>
<div>
Click on&nbsp;<em>&quot;Settings&quot;</em> then{" "}
<em>&quot;System&quot;</em> on the left side.
</div>
</li>
<li>
<div>
Toggle on{" "}
<em>&quot;Use hardware acceleration when available&quot;</em> if
this is not enabled.
</div>
</li>
<li>
<div>
Close and restart the browser for the change to take effect.
</div>
</li>
<ul>
<li>
<div>
You can also copy and paste this link{" "}
<CopyToClipboard.CopyToClipboard
text="edge://settings/system"
onCopy={() => setCopied2(true)}
>
<span className="text-blue">
edge://settings/system&nbsp;
{copied2 ? (
<span style={{ color: "red" }}>Copied.</span>
) : (
<span className="text-sm">
<FontAwesomeIcon icon={faCopy} />
</span>
)}
&nbsp;
</span>
</CopyToClipboard.CopyToClipboard>
into the address bar. This will bring you right to the system
settings.
</div>
</li>
</ul>
</ol>
<div className="my-4">
<b>Safari (Mac Only)</b>
</div>
<div>
As of macOS Catalina (version 10.15), Hardware acceleration is
automatically enabled, and there is no way turn this on and off. If you
are using an older version of MacOS, please use the steps below:
</div>
<ol>
<li>
<div>
Click on the &quot;Safari&quot; then &quot;Settings&quot; &gt;{" "}
</div>
</li>
<li>
<div>
Click &quot;Advanced&quot; on the top right of the Settings window
and ensure &quot;Use hardware acceleration&quot; is enabled
</div>
</li>
</ol>
<div>
If you are still having an issue with enabling hardware acceleration, we
recommend you update your graphics card drivers, if possible. If these
updates do not help, you may have to consider upgrading your graphics
card or playing on a different device.
</div>
</div>
);
};
@@ -62,4 +62,28 @@ export const VIDEO_MODELS: VideoModel[] = [
startFrame: true,
endFrame: false,
}),
new VideoModel({
id: "veo_3",
tauriId: "veo_3",
fullName: "Google Veo 3",
category: "video",
creator: ModelCreator.Google,
selectorName: "Google Veo 3",
selectorDescription: "Slow, high-quality model",
selectorBadges: ["2 min."],
startFrame: true,
endFrame: false,
}),
new VideoModel({
id: "veo_3_fast",
tauriId: "veo_3_fast",
fullName: "Google Veo 3 Fast",
category: "video",
creator: ModelCreator.Google,
selectorName: "Google Veo 3 Fast",
selectorDescription: "High-quality model (faster)",
selectorBadges: ["2 min."],
startFrame: true,
endFrame: false,
}),
];