mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-29 13:02:51 -04:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45f09f0262 | ||
|
|
851aba7e06 | ||
|
|
8ff9323ae6 | ||
|
|
50b085d450 | ||
|
|
1b70ee7394 | ||
|
|
1c3a176705 | ||
|
|
70c305f78d | ||
|
|
1c3178d073 | ||
|
|
1243e755fe | ||
|
|
d551e48a23 | ||
|
|
5cb608fad6 | ||
|
|
d5ae28ca4c | ||
|
|
8313558795 | ||
|
|
95173e032f | ||
|
|
b0f6551623 |
+35
-5
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n id,\n task_status,\n task_type,\n model_type,\n provider,\n provider_job_id,\n frontend_caller,\n frontend_subscriber_id,\n frontend_subscriber_payload,\n created_at as \"created_at: DateTime<Utc>\",\n updated_at as \"updated_at: DateTime<Utc>\",\n completed_at as \"completed_at: DateTime<Utc>\"\n FROM tasks\n WHERE is_dismissed_by_user == 0\n ",
|
||||
"query": "\n SELECT\n id,\n task_status,\n task_type,\n model_type,\n provider,\n provider_job_id,\n frontend_caller,\n frontend_subscriber_id,\n frontend_subscriber_payload,\n on_complete_primary_media_file_token,\n on_complete_primary_media_file_class,\n on_complete_batch_token,\n on_complete_primary_media_file_cdn_url,\n on_complete_primary_media_file_thumbnail_url_template,\n created_at as \"created_at: DateTime<Utc>\",\n updated_at as \"updated_at: DateTime<Utc>\",\n completed_at as \"completed_at: DateTime<Utc>\"\n FROM tasks\n WHERE is_dismissed_by_user == 0\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -49,18 +49,43 @@
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "created_at: DateTime<Utc>",
|
||||
"name": "on_complete_primary_media_file_token",
|
||||
"ordinal": 9,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "on_complete_primary_media_file_class",
|
||||
"ordinal": 10,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "on_complete_batch_token",
|
||||
"ordinal": 11,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "on_complete_primary_media_file_cdn_url",
|
||||
"ordinal": 12,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "on_complete_primary_media_file_thumbnail_url_template",
|
||||
"ordinal": 13,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "created_at: DateTime<Utc>",
|
||||
"ordinal": 14,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "updated_at: DateTime<Utc>",
|
||||
"ordinal": 10,
|
||||
"ordinal": 15,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "completed_at: DateTime<Utc>",
|
||||
"ordinal": 11,
|
||||
"ordinal": 16,
|
||||
"type_info": "Int64"
|
||||
}
|
||||
],
|
||||
@@ -77,10 +102,15 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "a142dc3fdf5b6e741c268d8f2e9996a28c214f5835681297d58a9431da8de939"
|
||||
"hash": "47f94c127abc20683b25c257aed3c2190b9bd29eb829d61392f08e10fd986767"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n UPDATE tasks\n SET\n task_status = ?,\n on_complete_batch_token = ?,\n on_complete_primary_media_file_token = ?,\n on_complete_primary_media_file_class = ?,\n on_complete_primary_media_file_cdn_url = ?,\n on_complete_primary_media_file_thumbnail_url_template = ?\n WHERE id = ?\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 7
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "50a2449b87abed7df3302a74ecd0e8ab87940077a247636680cb09b2fe488321"
|
||||
}
|
||||
Generated
+9
-3
@@ -660,7 +660,6 @@ dependencies = [
|
||||
"discord-rich-presence",
|
||||
"enums",
|
||||
"errors",
|
||||
"fal_client",
|
||||
"filesys",
|
||||
"futures 0.3.31",
|
||||
"idempotency",
|
||||
@@ -695,6 +694,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"url",
|
||||
"url_utils",
|
||||
"web_base64",
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
@@ -5573,7 +5573,6 @@ dependencies = [
|
||||
"mimetypes",
|
||||
"mysql_queries",
|
||||
"once_cell",
|
||||
"openai_sora_client",
|
||||
"opentelemetry",
|
||||
"opentelemetry-otlp",
|
||||
"opentelemetry_sdk",
|
||||
@@ -5586,7 +5585,6 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"shared_service_components",
|
||||
"sqlx",
|
||||
"subprocess",
|
||||
"subprocess_common",
|
||||
@@ -12460,6 +12458,14 @@ dependencies = [
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url_utils"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"url",
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
|
||||
@@ -84,6 +84,7 @@ members = [
|
||||
"crates/lib/testing",
|
||||
"crates/lib/tts_common",
|
||||
"crates/lib/url_config",
|
||||
"crates/lib/url_utils",
|
||||
"crates/lib/user_input_common",
|
||||
"crates/lib/web_scrapers",
|
||||
#"crates/lib/api_clients/fakeyou_client",
|
||||
@@ -210,6 +211,7 @@ subprocess_common = { path = "crates/lib/subprocess_common" }
|
||||
test_data = { path = "crates/lib/test_data" }
|
||||
testing = { path = "crates/lib/testing" }
|
||||
tokens = { path = "crates/schema/public/tokens" }
|
||||
url_utils = { path = "crates/lib/url_utils" }
|
||||
web_base64 = { path = "crates/lib/data/web_base64" }
|
||||
|
||||
# External libs
|
||||
|
||||
@@ -10,6 +10,13 @@
|
||||
-- tasks_v2.sqlite - added model_type (nullable)
|
||||
-- tasks_v3.sqlite - added frontend_caller, comments
|
||||
-- tasks_v4.sqlite - added is_dismissed_by_user
|
||||
-- tasks_v5.sqlite - added five fields:
|
||||
-- on_complete_batch_token,
|
||||
-- on_complete_primary_media_file_token,
|
||||
-- on_complete_primary_media_file_class,
|
||||
-- on_complete_primary_media_file_cdn_url,
|
||||
-- on_complete_primary_media_file_thumbnail_url_template,
|
||||
--
|
||||
|
||||
CREATE TABLE tasks (
|
||||
-- Task auto-incrementing primary key.
|
||||
@@ -54,6 +61,29 @@ CREATE TABLE tasks (
|
||||
-- Whether the user has dismissed the task from view.
|
||||
is_dismissed_by_user INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
-- OPTIONAL.
|
||||
-- When the generation completes, the batch token (if any).
|
||||
-- This will point to a collection of multiple files.
|
||||
on_complete_batch_token TEXT,
|
||||
|
||||
-- OPTIONAL.
|
||||
-- When the generation completes, the media token of the "primary" media file.
|
||||
-- We'll probably always generate homogenous types, but just in case this is the first or primary item in a batch.
|
||||
on_complete_primary_media_file_token TEXT,
|
||||
|
||||
-- OPTIONAL.
|
||||
-- When the generation completes, the file type of the primary media file.
|
||||
-- We'll probably always generate homogenous types, but just in case this is the first or primary item in a batch.
|
||||
on_complete_primary_media_file_class TEXT,
|
||||
|
||||
-- OPTIONAL.
|
||||
-- The URL to the media file.
|
||||
on_complete_primary_media_file_cdn_url TEXT,
|
||||
|
||||
-- OPTIONAL.
|
||||
-- The URL template for the thumbnail of the primary media file.
|
||||
on_complete_primary_media_file_thumbnail_url_template TEXT,
|
||||
|
||||
created_at INTEGER NOT NULL DEFAULT (unixepoch('now')),
|
||||
updated_at INTEGER NOT NULL DEFAULT (unixepoch('now')),
|
||||
completed_at INTEGER DEFAULT NULL
|
||||
|
||||
@@ -6,4 +6,5 @@ pub mod jobs;
|
||||
pub mod media_file;
|
||||
pub mod prompts;
|
||||
pub mod stripe_artcraft;
|
||||
pub mod subscriptions;
|
||||
pub mod subscriptions;
|
||||
pub mod utils;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
use crate::common::responses::media_links::MediaLinks;
|
||||
|
||||
/// Get the thumbnail template for either images or videos.
|
||||
pub fn media_links_to_thumbnail_template(links: &MediaLinks) -> Option<&str> {
|
||||
// NB: We only populate video previews for video tasks.
|
||||
if let Some(video) = links.maybe_video_previews.as_ref() {
|
||||
return Some(&video.animated_thumbnail_template);
|
||||
}
|
||||
|
||||
// Image case - use the image thumbnail template if it exists.
|
||||
links.maybe_thumbnail_template.as_deref()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod media_links_to_thumbnail_template;
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
/// This user agent is tied to the sentinel generation.
|
||||
/// If we need to change it, we may need to change sentinel generation too.
|
||||
pub (crate) const USER_AGENT : &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36";
|
||||
pub (crate) const CLIENT_USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36";
|
||||
|
||||
|
||||
// NB: from sentinel token request -
|
||||
|
||||
@@ -2,4 +2,5 @@ pub mod sora_cookies;
|
||||
pub mod sora_credential_builder;
|
||||
pub mod sora_credential_set;
|
||||
pub mod sora_jwt_bearer_token;
|
||||
pub mod sora_sentinel;
|
||||
pub mod sora_sentinel;
|
||||
pub mod sora_sentinel_token;
|
||||
@@ -2,13 +2,20 @@ use crate::creds::sora_cookies::SoraCookies;
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::creds::sora_jwt_bearer_token::SoraJwtBearerToken;
|
||||
use crate::creds::sora_sentinel::SoraSentinel;
|
||||
use crate::creds::sora_sentinel_token::SoraSentinelToken;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
|
||||
pub struct SoraCredentialBuilder {
|
||||
cookies: Option<String>,
|
||||
|
||||
jwt_bearer_token: Option<String>,
|
||||
|
||||
#[deprecated(note="use sora_sentinel_token instead")]
|
||||
sora_sentinel: Option<String>,
|
||||
|
||||
/// This is the newer sentinel token. Gradually phase out the older one.
|
||||
sora_sentinel_token: Option<SoraSentinelToken>,
|
||||
}
|
||||
|
||||
impl SoraCredentialBuilder {
|
||||
@@ -17,6 +24,7 @@ impl SoraCredentialBuilder {
|
||||
cookies: None,
|
||||
jwt_bearer_token: None,
|
||||
sora_sentinel: None,
|
||||
sora_sentinel_token: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +45,11 @@ impl SoraCredentialBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_sora_sentinel_token(mut self, sentinel_token: &SoraSentinelToken) -> Self {
|
||||
self.sora_sentinel_token = Some(sentinel_token.clone());
|
||||
self
|
||||
}
|
||||
|
||||
// Mutable methods
|
||||
|
||||
pub fn set_cookies(&mut self, cookies: &str) {
|
||||
@@ -50,6 +63,10 @@ impl SoraCredentialBuilder {
|
||||
pub fn set_sora_sentinel(&mut self, sentinel: &str) {
|
||||
self.sora_sentinel = Some(sentinel.to_string());
|
||||
}
|
||||
|
||||
pub fn set_sora_sentinel_token(&mut self, sentinel_token: &SoraSentinelToken) {
|
||||
self.sora_sentinel_token = Some(sentinel_token.clone());
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<SoraCredentialSet, SoraError> {
|
||||
let cookies = match self.cookies {
|
||||
@@ -67,10 +84,16 @@ impl SoraCredentialBuilder {
|
||||
None => None,
|
||||
};
|
||||
|
||||
let sora_sentinel_token = match self.sora_sentinel_token {
|
||||
Some(s) => Some(s),
|
||||
None => None,
|
||||
};
|
||||
|
||||
Ok(SoraCredentialSet {
|
||||
cookies,
|
||||
jwt_bearer_token,
|
||||
sora_sentinel,
|
||||
sora_sentinel_token,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,32 @@ use crate::creds::sora_cookies::SoraCookies;
|
||||
use crate::creds::sora_credential_builder::SoraCredentialBuilder;
|
||||
use crate::creds::sora_jwt_bearer_token::SoraJwtBearerToken;
|
||||
use crate::creds::sora_sentinel::SoraSentinel;
|
||||
use crate::creds::sora_sentinel_token::SoraSentinelToken;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SoraCredentialSet {
|
||||
pub cookies: SoraCookies,
|
||||
pub jwt_bearer_token: Option<SoraJwtBearerToken>,
|
||||
|
||||
#[deprecated(note="use sora_sentinel_token instead")]
|
||||
pub sora_sentinel: Option<SoraSentinel>,
|
||||
|
||||
// NB: This is the newer sentinel token. Gradually phase out the older one.
|
||||
pub sora_sentinel_token: Option<SoraSentinelToken>,
|
||||
}
|
||||
|
||||
impl SoraCredentialSet {
|
||||
pub fn initialize(
|
||||
cookies: SoraCookies,
|
||||
bearer: Option<SoraJwtBearerToken>,
|
||||
sentinel: Option<SoraSentinel>
|
||||
sentinel: Option<SoraSentinel>,
|
||||
sentinel_token: Option<SoraSentinelToken>,
|
||||
) -> Self {
|
||||
Self {
|
||||
cookies,
|
||||
jwt_bearer_token: bearer,
|
||||
sora_sentinel: sentinel,
|
||||
sora_sentinel_token: sentinel_token,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +36,7 @@ impl SoraCredentialSet {
|
||||
cookies,
|
||||
jwt_bearer_token: None,
|
||||
sora_sentinel: None,
|
||||
sora_sentinel_token: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +55,9 @@ impl SoraCredentialSet {
|
||||
if let Some(sentinel) = &self.sora_sentinel {
|
||||
builder = builder.with_sora_sentinel(sentinel.as_str());
|
||||
}
|
||||
if let Some(sentinel_token) = &self.sora_sentinel_token {
|
||||
builder = builder.with_sora_sentinel_token(sentinel_token);
|
||||
}
|
||||
builder
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
/// Sora Sentinel tokens can be generated without cookies or JWTs, and are needed for some API calls.
|
||||
#[derive(Clone)]
|
||||
#[deprecated(note="use SoraSentinelToken instead")]
|
||||
pub struct SoraSentinel {
|
||||
sentinel: String,
|
||||
|
||||
@@ -13,14 +14,17 @@ impl SoraSentinel {
|
||||
SoraSentinel { sentinel }
|
||||
}
|
||||
|
||||
#[deprecated(note="use SoraSentinelToken instead")]
|
||||
pub fn get_sentinel(&self) -> &str {
|
||||
&self.sentinel
|
||||
}
|
||||
|
||||
#[deprecated(note="use SoraSentinelToken instead")]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.sentinel
|
||||
}
|
||||
|
||||
#[deprecated(note="use SoraSentinelToken instead")]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.sentinel.as_bytes()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use chrono::{DateTime, TimeDelta, Utc};
|
||||
use log::{error, warn};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct SoraSentinelToken {
|
||||
/// The token sent over the wire.
|
||||
/// (The other fields in this struct are metadata.)
|
||||
pub token: RawSoraSentinelToken,
|
||||
|
||||
/// User agent used to get the token.
|
||||
pub browser_user_agent: String,
|
||||
|
||||
/// Clientside time when it was generated (client-side).
|
||||
pub generated_at: DateTime<Utc>,
|
||||
|
||||
/// Seconds of validity (reported by the server, or our best guess).
|
||||
/// If the server doesn't tell us, we'll fill it in with our best guess.
|
||||
pub expires_in_seconds: u32,
|
||||
}
|
||||
|
||||
/// This is what is sent over the wire in requests.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RawSoraSentinelToken {
|
||||
/// "Proof of Work" token (we generated this clientside)
|
||||
#[serde(rename = "p")]
|
||||
pub p: String,
|
||||
|
||||
/// Random UUID (we generated this clientside)
|
||||
#[serde(rename = "id")]
|
||||
pub id: String,
|
||||
|
||||
/// Frontend UI flow (this is the website flow used)
|
||||
#[serde(rename = "flow")]
|
||||
pub flow: String,
|
||||
|
||||
/// Turnstile dx from the response.
|
||||
/// response["turnstile"]["dx"]
|
||||
#[serde(rename = "t")]
|
||||
pub t: String,
|
||||
|
||||
/// Token from the response.
|
||||
/// response["token"]
|
||||
#[serde(rename = "c")]
|
||||
pub c: String,
|
||||
}
|
||||
|
||||
impl SoraSentinelToken {
|
||||
/// Check to see if the sentinel token is expired and in need of a refresh.
|
||||
pub fn is_expired(&self) -> bool {
|
||||
let now = Utc::now();
|
||||
|
||||
let maybe_expires_at = self.generated_at
|
||||
.checked_add_signed(TimeDelta::seconds(self.expires_in_seconds as i64));
|
||||
|
||||
let expires_at = match maybe_expires_at {
|
||||
Some(expires_at) => expires_at,
|
||||
None => {
|
||||
error!("Could not compute expires_at for sentinel token!");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
if now >= expires_at {
|
||||
return true;
|
||||
}
|
||||
|
||||
if expires_at.signed_duration_since(now).num_seconds() < 30 {
|
||||
warn!("Less than 30 seconds remaining on sentinel token, treating as expired.");
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Convert to JSON, which is the raw over-the-wire format Sora consumes.
|
||||
pub fn to_request_header_json(&self) -> Result<String, SoraClientError> {
|
||||
self.token.to_request_header_json()
|
||||
}
|
||||
|
||||
pub fn to_persistent_storage_json(&self) -> Result<String, SoraClientError> {
|
||||
serde_json::to_string(self)
|
||||
.map_err(|err| SoraClientError::CouldNotSerializeSentinelTokenStore(err))
|
||||
}
|
||||
|
||||
pub fn from_persistent_storage_json(json: &str) -> Result<Self, SoraClientError> {
|
||||
serde_json::from_str(json)
|
||||
.map_err(|err| SoraClientError::CouldNotDeserializeSentinelTokenStore { error: err, raw_json: json.to_owned() })
|
||||
}
|
||||
}
|
||||
|
||||
impl RawSoraSentinelToken {
|
||||
/// Convert to JSON, which is the raw over-the-wire format Sora consumes.
|
||||
pub fn to_request_header_json(&self) -> Result<String, SoraClientError> {
|
||||
serde_json::to_string(self)
|
||||
.map_err(|err| SoraClientError::CouldNotSerializeSentinelToken(err))
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,18 @@ pub enum SoraClientError {
|
||||
|
||||
/// An error was encountered in building the Wreq client
|
||||
WreqClientError(wreq::Error),
|
||||
|
||||
/// Error serializing the sentinel token to JSON
|
||||
CouldNotSerializeSentinelToken(serde_json::Error),
|
||||
|
||||
/// Error serializing the sentinel store token to JSON (typically for persistent storage).
|
||||
CouldNotSerializeSentinelTokenStore(serde_json::Error),
|
||||
|
||||
/// Error deserializing the sentinel token from JSON (typically for persistent storage).
|
||||
CouldNotDeserializeSentinelTokenStore {
|
||||
error: serde_json::Error,
|
||||
raw_json: String
|
||||
},
|
||||
}
|
||||
|
||||
impl Error for SoraClientError {}
|
||||
@@ -48,6 +60,11 @@ impl Display for SoraClientError {
|
||||
Self::SoraCredentialBuilderError(msg) => write!(f, "Sora Credential Builder error: {}", msg),
|
||||
Self::UrlParseError(err) => write!(f, "URL parse error: {}", err),
|
||||
Self::WreqClientError(err) => write!(f, "Wreq client error (during client creation): {}", err),
|
||||
Self::CouldNotSerializeSentinelToken(err) => write!(f, "Could not serialize sentinel token to JSON: {}", err),
|
||||
Self::CouldNotSerializeSentinelTokenStore(err) => write!(f, "Could not serialize sentinel token store to JSON: {}", err),
|
||||
Self::CouldNotDeserializeSentinelTokenStore { error, raw_json} => {
|
||||
write!(f, "Could not deserialize sentinel token store from JSON: {:?}, raw JSON: {}", error, raw_json)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,13 @@ pub enum SoraSpecificApiError {
|
||||
/// }
|
||||
/// }
|
||||
UnauthorizedCookieOrBearerExpired,
|
||||
|
||||
/// The Sora sentinel token response is missing fields
|
||||
SentinelResponseIsMissingFields {
|
||||
missing_token: bool,
|
||||
missing_turnstile: bool,
|
||||
raw_response: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl Error for SoraSpecificApiError {}
|
||||
@@ -53,6 +60,9 @@ impl Display for SoraSpecificApiError {
|
||||
Self::TokenExpiredError => write!(f, "JWT token has expired. Please refresh the token."),
|
||||
Self::TooManyConcurrentTasks => write!(f, "Too many concurrent tasks. Please wait."),
|
||||
Self::UnauthorizedCookieOrBearerExpired => write!(f, "Unauthorized: cookie and/or bearer token expired"),
|
||||
Self::SentinelResponseIsMissingFields { missing_token: token_missing, missing_turnstile: turnstile_missing, raw_response } => {
|
||||
write!(f, "Sentinel response is missing fields. token_missing: {}, turnstile_missing: {}. Raw response: {}", token_missing, turnstile_missing, raw_response)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -1,15 +1,23 @@
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::recipes::utils::maybe_renew_sentinel_token::maybe_renew_sentinel_token;
|
||||
use crate::recipes::utils::maybe_renew_session_jwt::maybe_renew_session_jwt;
|
||||
use crate::requests::generate_sora2_video::generate_sora2_video::{generate_sora2_video, GenerateSora2VideoArgs, GenerateSora2VideoResponse};
|
||||
use std::io::Write;
|
||||
|
||||
/// Generate Sora 2 video with session auto-renewal.
|
||||
/// If a new sora credential is returned, replace the old one with the new one.
|
||||
pub async fn generate_sora2_video_with_session_auto_renew(
|
||||
args: GenerateSora2VideoArgs<'_>,
|
||||
) -> Result<(GenerateSora2VideoResponse, Option<SoraCredentialSet>), SoraError> {
|
||||
|
||||
let mut maybe_new_creds = maybe_renew_session_jwt(&args.credentials).await?;
|
||||
|
||||
let use_creds = maybe_new_creds.as_ref()
|
||||
.unwrap_or_else(|| &args.credentials);
|
||||
|
||||
let mut maybe_new_creds = maybe_renew_sentinel_token(use_creds).await?;
|
||||
|
||||
let use_creds = maybe_new_creds.as_ref()
|
||||
.unwrap_or_else(|| &args.credentials);
|
||||
|
||||
@@ -37,7 +45,7 @@ mod tests {
|
||||
let creds = get_test_credentials()?;
|
||||
|
||||
let args = GenerateSora2VideoArgs {
|
||||
prompt: "a dog playing tennis",
|
||||
prompt: "a Golden Retriever playing basketball at Venice Beach during sunset",
|
||||
credentials: &creds,
|
||||
request_timeout: None,
|
||||
orientation: Orientation::Landscape,
|
||||
|
||||
+24
-9
@@ -4,12 +4,13 @@ use crate::creds::sora_sentinel::SoraSentinel;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::requests::auth_bearer::generate_bearer_jwt_with_cookie::generate_bearer_jwt_with_cookie;
|
||||
use crate::requests::auth_sentinel::generate_sentinel_token::generate_sentinel_token;
|
||||
use crate::requests::auth_sentinel_2::generate_sentinel_token_2::generate_sentinel_token_2;
|
||||
use chrono::{DateTime, TimeDelta, Utc};
|
||||
use errors::AnyhowResult;
|
||||
use log::info;
|
||||
use std::ops::Sub;
|
||||
|
||||
const EXPIRATION_DEADLINE : TimeDelta = TimeDelta::hours(12);
|
||||
const JWT_EXPIRATION_DEADLINE: TimeDelta = TimeDelta::hours(12);
|
||||
|
||||
/// Call this at session startup to generate the tokens for the first time.
|
||||
/// Call this periodically to refresh the JWT as it nears its expiration date.
|
||||
@@ -17,11 +18,10 @@ const EXPIRATION_DEADLINE : TimeDelta = TimeDelta::hours(12);
|
||||
#[deprecated(note="The sentinel appears to be going away. Use `maybe_renew_session_jwt` instead for newer '2.0' code.")]
|
||||
pub async fn maybe_upgrade_or_renew_session(sora_credentials: &mut SoraCredentialSet) -> Result<bool, SoraError> {
|
||||
let mut refresh_jwt = !sora_credentials.jwt_bearer_token.is_some();
|
||||
let refresh_sentinel = !sora_credentials.sora_sentinel.is_some();
|
||||
|
||||
|
||||
if let Some(jwt) = &sora_credentials.jwt_bearer_token {
|
||||
let now = Utc::now();
|
||||
let refresh_deadline = now.sub(EXPIRATION_DEADLINE);
|
||||
let refresh_deadline = now.sub(JWT_EXPIRATION_DEADLINE);
|
||||
if jwt.jwt_claims().expiration.lt(&now) {
|
||||
info!("JWT expiration time has elapsed.");
|
||||
refresh_jwt = true;
|
||||
@@ -34,6 +34,7 @@ pub async fn maybe_upgrade_or_renew_session(sora_credentials: &mut SoraCredentia
|
||||
let mut credential_updated = false;
|
||||
|
||||
if refresh_jwt {
|
||||
info!("Refreshing JWT...");
|
||||
let cookies = sora_credentials.cookies.as_str();
|
||||
let token = generate_bearer_jwt_with_cookie(cookies).await?;
|
||||
|
||||
@@ -43,11 +44,25 @@ pub async fn maybe_upgrade_or_renew_session(sora_credentials: &mut SoraCredentia
|
||||
credential_updated = true;
|
||||
}
|
||||
|
||||
if refresh_sentinel {
|
||||
info!("Generating new sentinel...");
|
||||
let token = generate_sentinel_token().await?;
|
||||
let token = SoraSentinel::new(token);
|
||||
sora_credentials.sora_sentinel = Some(token);
|
||||
//let refresh_sentinel = !sora_credentials.sora_sentinel.is_some();
|
||||
//if refresh_sentinel {
|
||||
// info!("Generating new sentinel...");
|
||||
// let token = generate_sentinel_token().await?;
|
||||
// let token = SoraSentinel::new(token);
|
||||
// sora_credentials.sora_sentinel = Some(token);
|
||||
// credential_updated = true;
|
||||
//}
|
||||
|
||||
let refresh_sentinel_token = sora_credentials.sora_sentinel_token
|
||||
.as_ref()
|
||||
.map(|t| t.is_expired())
|
||||
.unwrap_or(true);
|
||||
|
||||
if refresh_sentinel_token {
|
||||
info!("Refreshing sentinel token...");
|
||||
let token = generate_sentinel_token_2().await?;
|
||||
sora_credentials.sora_sentinel = Some(SoraSentinel::new(token.to_request_header_json()?)); // Legacy
|
||||
sora_credentials.sora_sentinel_token = Some(token); // New
|
||||
credential_updated = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::creds::sora_sentinel_token::SoraSentinelToken;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::requests::auth_sentinel_2::generate_sentinel_token_2::generate_sentinel_token_2;
|
||||
use log::info;
|
||||
|
||||
/// Returns a new sentinel token if one was generated, or None if no update was needed.
|
||||
pub async fn maybe_renew_sentinel_token(creds: &SoraCredentialSet) -> Result<Option<SoraCredentialSet>, SoraError> {
|
||||
let mut refresh_sentinel = false;
|
||||
|
||||
match creds.sora_sentinel_token.as_ref() {
|
||||
None => {
|
||||
info!("Sentinel token not set. It needs to be generated...");
|
||||
refresh_sentinel = true;
|
||||
}
|
||||
Some(sentinel) => {
|
||||
let expired = sentinel.is_expired();
|
||||
if expired {
|
||||
info!("Sentinel token is expired. It needs to be renewed...");
|
||||
refresh_sentinel = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !refresh_sentinel {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let sentinel = generate_sentinel_token_2().await?;
|
||||
|
||||
let mut new_creds = creds.clone();
|
||||
new_creds.sora_sentinel_token = Some(sentinel);
|
||||
|
||||
Ok(Some(new_creds))
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::creds::sora_jwt_bearer_token::SoraJwtBearerToken;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
use crate::requests::auth_bearer::generate_bearer_jwt_with_cookie::generate_bearer_jwt_with_cookie;
|
||||
use chrono::{TimeDelta, Utc};
|
||||
use log::{info, warn};
|
||||
use std::io::Write;
|
||||
use std::ops::Sub;
|
||||
|
||||
const EXPIRATION_DEADLINE : TimeDelta = TimeDelta::hours(12);
|
||||
@@ -15,17 +15,17 @@ pub async fn maybe_renew_session_jwt(creds: &SoraCredentialSet) -> Result<Option
|
||||
|
||||
match creds.jwt_bearer_token.as_ref() {
|
||||
None => {
|
||||
info!("JWT not set.");
|
||||
info!("JWT not set. It needs to be generated...");
|
||||
refresh_jwt = true;
|
||||
}
|
||||
Some(jwt) => {
|
||||
let now = Utc::now();
|
||||
let refresh_deadline = now.sub(EXPIRATION_DEADLINE);
|
||||
if jwt.jwt_claims().expiration.lt(&now) {
|
||||
info!("JWT expiration time has elapsed.");
|
||||
info!("JWT expiration time has elapsed. It needs to be renewed...");
|
||||
refresh_jwt = true;
|
||||
} else if jwt.jwt_claims().expiration.lt(&refresh_deadline) {
|
||||
info!("JWT expiration time is under the expiration deadline.");
|
||||
info!("JWT expiration time is under the expiration deadline. It needs to be renewed...");
|
||||
refresh_jwt = true;
|
||||
}
|
||||
}
|
||||
@@ -34,13 +34,13 @@ pub async fn maybe_renew_session_jwt(creds: &SoraCredentialSet) -> Result<Option
|
||||
if !refresh_jwt {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
|
||||
info!("Refreshing JWT...");
|
||||
let token = generate_bearer_jwt_with_cookie(creds.cookies.as_str()).await?;
|
||||
|
||||
info!("Parsing JWT bearer token...");
|
||||
let token = SoraJwtBearerToken::new(token)?;
|
||||
|
||||
|
||||
let mut updated_creds = creds.clone();
|
||||
updated_creds.jwt_bearer_token = Some(token);
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod maybe_renew_session_jwt;
|
||||
pub mod maybe_renew_session_jwt;
|
||||
pub mod maybe_renew_sentinel_token;
|
||||
+50
-36
File diff suppressed because one or more lines are too long
+7
-2
@@ -1,5 +1,5 @@
|
||||
use super::request::GenerateSentinelRefreshRequest;
|
||||
use crate::constants::user_agent::USER_AGENT;
|
||||
use crate::constants::user_agent::CLIENT_USER_AGENT;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::error::sora_generic_api_error::SoraGenericApiError;
|
||||
use crate::utils_internal::classify_general_http_error::classify_general_http_error;
|
||||
@@ -43,12 +43,16 @@ pub struct SentinelResponse {
|
||||
}
|
||||
|
||||
|
||||
/// Note: This looks like an incomplete implementation of the original Python implementation.
|
||||
/// Curiously, it worked with Sora 1.0 (and continues to work), but it fails for Sora 2.0 while the
|
||||
/// unchanged Python implementation works. We'll be deprecating this.
|
||||
#[deprecated(note="use new sentinel token flow")]
|
||||
pub async fn generate_sentinel_token() -> Result<String, SoraError> {
|
||||
let (_request, base64_request) = GenerateSentinelRefreshRequest::new().with_fourth_and_tenth();
|
||||
let request = SentinelRequest::new(base64_request);
|
||||
let client = Client::new();
|
||||
let response = client.post(SORA_IMAGE_GEN_URL)
|
||||
.header("User-Agent", USER_AGENT)
|
||||
.header("User-Agent", CLIENT_USER_AGENT)
|
||||
.header("Content-Type", "application/json")
|
||||
.json(&request)
|
||||
.send()
|
||||
@@ -85,6 +89,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // Only manually trigger this
|
||||
async fn test_generate_token() {
|
||||
let token = generate_sentinel_token().await.unwrap();
|
||||
println!("{}", token);
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
use crate::requests::auth_sentinel_2::response::SentinelResponse;
|
||||
use chrono::{DateTime, TimeDelta, Utc};
|
||||
use log::{info, warn};
|
||||
use std::f32::consts::E;
|
||||
|
||||
// Sora's servers typically tell us to expire in nine minutes (540 seconds).
|
||||
const DEFAULT_EXPIRE_SECONDS : u32 = 60 * 3; // 3 minutes.
|
||||
|
||||
pub (super) struct ExtractedExpiry {
|
||||
pub (super) generated_at: DateTime<Utc>,
|
||||
pub (super) expires_in_seconds: u32,
|
||||
}
|
||||
|
||||
pub (super) fn extract_expiry(generated_at: DateTime<Utc>, response: &SentinelResponse) -> ExtractedExpiry {
|
||||
info!("Sentinel expires at: {:?} (in {:?} seconds)", response.expire_at, response.expire_after);
|
||||
|
||||
const LOWER_BOUNDS_SECONDS: u32 = 60;
|
||||
const UPPER_BOUNDS_SECONDS: u32 = 60 * 60;
|
||||
|
||||
// Trust the `expire_at` timestamp first.
|
||||
let mut maybe_expires_at = response.expire_at
|
||||
.map(|timestamp| DateTime::from_timestamp(timestamp as i64, 0))
|
||||
.flatten();
|
||||
|
||||
if let Some(expires_at) = maybe_expires_at {
|
||||
let seconds = expires_at.signed_duration_since(generated_at).num_seconds();
|
||||
|
||||
const LOWER_BOUNDS_SECONDS_I64: i64 = LOWER_BOUNDS_SECONDS as i64;
|
||||
const UPPER_BOUNDS_SECONDS_I64: i64 = UPPER_BOUNDS_SECONDS as i64;
|
||||
|
||||
if seconds.is_positive()
|
||||
&& seconds > LOWER_BOUNDS_SECONDS_I64
|
||||
&& seconds < UPPER_BOUNDS_SECONDS_I64
|
||||
{
|
||||
info!("Using sentinel expire_at timestamp, expires in {} seconds (at {})", seconds, expires_at);
|
||||
|
||||
return ExtractedExpiry {
|
||||
generated_at,
|
||||
expires_in_seconds: seconds as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut expires_in_seconds = response.expire_after
|
||||
.unwrap_or_else(|| {
|
||||
warn!("No default expire_after from sentinel response, defaulting to {} seconds", DEFAULT_EXPIRE_SECONDS);
|
||||
DEFAULT_EXPIRE_SECONDS
|
||||
});
|
||||
|
||||
if expires_in_seconds < LOWER_BOUNDS_SECONDS {
|
||||
warn!("Warning: sentinel token expires very soon: {} seconds, defaulting to {} seconds", expires_in_seconds, DEFAULT_EXPIRE_SECONDS);
|
||||
expires_in_seconds = DEFAULT_EXPIRE_SECONDS;
|
||||
} else if expires_in_seconds > UPPER_BOUNDS_SECONDS {
|
||||
warn!("Warning: sentinel token expires very late: {} seconds, defaulting to {} seconds", expires_in_seconds, DEFAULT_EXPIRE_SECONDS);
|
||||
expires_in_seconds = DEFAULT_EXPIRE_SECONDS;
|
||||
}
|
||||
|
||||
ExtractedExpiry {
|
||||
generated_at,
|
||||
expires_in_seconds,
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
use crate::constants::user_agent::CLIENT_USER_AGENT;
|
||||
use crate::creds::sora_sentinel_token::{RawSoraSentinelToken, SoraSentinelToken};
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::error::sora_generic_api_error::SoraGenericApiError;
|
||||
use crate::error::sora_specific_api_error::SoraSpecificApiError;
|
||||
use crate::requests::auth_sentinel::request::GenerateSentinelRefreshRequest;
|
||||
use crate::requests::auth_sentinel_2::extract_expiry::extract_expiry;
|
||||
use crate::requests::auth_sentinel_2::request::SentinelRequest;
|
||||
use crate::requests::auth_sentinel_2::response::SentinelResponse;
|
||||
use crate::utils_internal::classify_general_http_error::classify_general_http_error;
|
||||
use chrono::{DateTime, NaiveDateTime, TimeDelta, TimeZone, Utc};
|
||||
use errors::AnyhowResult;
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{debug, error, info};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::io::Write;
|
||||
use thiserror::Error;
|
||||
use wreq::header::{ACCEPT, ACCEPT_LANGUAGE, AUTHORIZATION, CONTENT_TYPE, COOKIE, ORIGIN, REFERER, USER_AGENT};
|
||||
use wreq::Client;
|
||||
|
||||
const SORA_SENTINEL_ENDPOINT: &str = "https://chatgpt.com/backend-api/sentinel/req";
|
||||
|
||||
|
||||
/// This generates a Sentinel Token that works with Sora 1.0 and Sora 2.0 consumer products.
|
||||
pub async fn generate_sentinel_token_2() -> Result<SoraSentinelToken, SoraError> {
|
||||
let (_request, base64_request) = GenerateSentinelRefreshRequest::new().with_fourth_and_tenth();
|
||||
|
||||
let request = SentinelRequest::new(base64_request);
|
||||
let client = Client::new();
|
||||
|
||||
let request_builder = client.post(SORA_SENTINEL_ENDPOINT)
|
||||
.header(USER_AGENT, CLIENT_USER_AGENT)
|
||||
.header(ACCEPT, "*/*")
|
||||
.header(ACCEPT_LANGUAGE, "en-US,en;q=0.9")
|
||||
// TODO: Accept-Encoding
|
||||
// TODO: Cookie
|
||||
.header(REFERER, "https://chatgpt.com/backend-api/sentinel/frame.html")
|
||||
.header(CONTENT_TYPE, "application/json")
|
||||
.header(ORIGIN, "https://chatgpt.com")
|
||||
.header("sec-gpc", "1")
|
||||
.header("sec-fetch-dest", "empty")
|
||||
.header("sec-fetch-mode", "cors")
|
||||
.header("sec-fetch-site", "same-origin")
|
||||
.header("priority", "u=4")
|
||||
.header("te", "trailers");
|
||||
|
||||
let response = request_builder
|
||||
.json(&request)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Client failed to generate sentinel token: {:?}", err);
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
error!("Failed to generate sentinel: {}", response.status());
|
||||
let error = classify_general_http_error(response).await;
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
let text_body = &response.text().await
|
||||
.map_err(|err| {
|
||||
error!("sora error reading sentinel token text body: {}", err);
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
debug!("Sentinel endpoint response body: {}", text_body);
|
||||
|
||||
let response = serde_json::from_str::<SentinelResponse>(&text_body)
|
||||
.map_err(|err| {
|
||||
error!("Failed to parse sentinel token response: {}", err);
|
||||
SoraGenericApiError::SerdeResponseParseErrorWithBody(err, text_body.to_string())
|
||||
})?;
|
||||
|
||||
let sentinel_token = into_sora_sentinel_token(request, response, text_body)?;
|
||||
|
||||
Ok(sentinel_token)
|
||||
}
|
||||
|
||||
fn into_sora_sentinel_token(request: SentinelRequest, response: SentinelResponse, raw_body: &str) -> Result<SoraSentinelToken, SoraSpecificApiError> {
|
||||
let expiry = extract_expiry(Utc::now(), &response);
|
||||
|
||||
let maybe_token = response.token;
|
||||
|
||||
let maybe_turnstile = response.turnstile
|
||||
.map(|turn| turn.dx)
|
||||
.flatten();
|
||||
|
||||
let mut missing_token = false;
|
||||
let mut missing_turnstile = false;
|
||||
|
||||
let (token, turnstile) = {
|
||||
let maybe_items = match (maybe_token, maybe_turnstile) {
|
||||
(Some(token), Some(turnstile)) => {
|
||||
Some((token, turnstile))
|
||||
}
|
||||
(None, None) => {
|
||||
missing_token = true;
|
||||
missing_turnstile = true;
|
||||
None
|
||||
}
|
||||
(None, Some(_turnstile)) => {
|
||||
missing_token = true;
|
||||
None
|
||||
}
|
||||
(Some(_token), None) => {
|
||||
missing_turnstile = true;
|
||||
None
|
||||
}
|
||||
};
|
||||
match maybe_items {
|
||||
Some((token, turnstile)) => (token, turnstile),
|
||||
_ => return Err(SoraSpecificApiError::SentinelResponseIsMissingFields {
|
||||
missing_token,
|
||||
missing_turnstile,
|
||||
raw_response: raw_body.to_string(),
|
||||
}),
|
||||
}
|
||||
};
|
||||
|
||||
let sentinel_raw = RawSoraSentinelToken {
|
||||
p: request.p,
|
||||
id: request.id,
|
||||
flow: request.flow,
|
||||
t: turnstile,
|
||||
c: token,
|
||||
};
|
||||
|
||||
|
||||
let sentinel = SoraSentinelToken {
|
||||
token: sentinel_raw,
|
||||
browser_user_agent: CLIENT_USER_AGENT.to_string(),
|
||||
generated_at: expiry.generated_at,
|
||||
expires_in_seconds: expiry.expires_in_seconds,
|
||||
};
|
||||
|
||||
Ok(sentinel)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs::write;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // Only manually trigger this
|
||||
async fn test_generate_token_2() -> AnyhowResult<()> {
|
||||
let token = generate_sentinel_token_2().await?;
|
||||
println!("{:?}", token);
|
||||
assert_eq!(1, 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // Only manually trigger this
|
||||
async fn write_token_to_disk() -> AnyhowResult<()> {
|
||||
let filename = "/Users/bt/Artcraft/credentials/sora_sentinel_token_store.json";
|
||||
let token = generate_sentinel_token_2().await?;
|
||||
|
||||
let json = token.to_persistent_storage_json()?;
|
||||
|
||||
write(filename, &json)?;
|
||||
|
||||
assert_eq!(1, 2);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+357
@@ -0,0 +1,357 @@
|
||||
cores = [8, 16, 24, 32]
|
||||
cached_scripts = ["https://cdn.oaistatic.com/_next/static/cXh69klOLzS0Gy2joLDRS/_ssgManifest.js?dpl=453ebaec0d44c2decab71692e1bfe39be35a24b3"]
|
||||
|
||||
cached_dpl = ["prod-f501fe933b3edf57aea882da888e1a544df99840"]
|
||||
|
||||
navigator_key = [
|
||||
"registerProtocolHandler−function registerProtocolHandler() { [native code] }",
|
||||
"storage−[object StorageManager]",
|
||||
"locks−[object LockManager]",
|
||||
"appCodeName−Mozilla",
|
||||
"permissions−[object Permissions]",
|
||||
"share−function share() { [native code] }",
|
||||
"webdriver−false",
|
||||
"managed−[object NavigatorManagedData]",
|
||||
"canShare−function canShare() { [native code] }",
|
||||
"vendor−Google Inc.",
|
||||
"vendor−Google Inc.",
|
||||
"mediaDevices−[object MediaDevices]",
|
||||
"vibrate−function vibrate() { [native code] }",
|
||||
"storageBuckets−[object StorageBucketManager]",
|
||||
"mediaCapabilities−[object MediaCapabilities]",
|
||||
"getGamepads−function getGamepads() { [native code] }",
|
||||
"bluetooth−[object Bluetooth]",
|
||||
"share−function share() { [native code] }",
|
||||
"cookieEnabled−true",
|
||||
"virtualKeyboard−[object VirtualKeyboard]",
|
||||
"product−Gecko",
|
||||
"mediaDevices−[object MediaDevices]",
|
||||
"canShare−function canShare() { [native code] }",
|
||||
"getGamepads−function getGamepads() { [native code] }",
|
||||
"product−Gecko",
|
||||
"xr−[object XRSystem]",
|
||||
"clipboard−[object Clipboard]",
|
||||
"storageBuckets−[object StorageBucketManager]",
|
||||
"unregisterProtocolHandler−function unregisterProtocolHandler() { [native code] }",
|
||||
"productSub−20030107",
|
||||
"login−[object NavigatorLogin]",
|
||||
"vendorSub−",
|
||||
"login−[object NavigatorLogin]",
|
||||
"getInstalledRelatedApps−function getInstalledRelatedApps() { [native code] }",
|
||||
"mediaDevices−[object MediaDevices]",
|
||||
"locks−[object LockManager]",
|
||||
"webkitGetUserMedia−function webkitGetUserMedia() { [native code] }",
|
||||
"vendor−Google Inc.",
|
||||
"xr−[object XRSystem]",
|
||||
"mediaDevices−[object MediaDevices]",
|
||||
"virtualKeyboard−[object VirtualKeyboard]",
|
||||
"virtualKeyboard−[object VirtualKeyboard]",
|
||||
"appName−Netscape",
|
||||
"storageBuckets−[object StorageBucketManager]",
|
||||
"presentation−[object Presentation]",
|
||||
"onLine−true",
|
||||
"mimeTypes−[object MimeTypeArray]",
|
||||
"credentials−[object CredentialsContainer]",
|
||||
"presentation−[object Presentation]",
|
||||
"getGamepads−function getGamepads() { [native code] }",
|
||||
"vendorSub−",
|
||||
"virtualKeyboard−[object VirtualKeyboard]",
|
||||
"serviceWorker−[object ServiceWorkerContainer]",
|
||||
"xr−[object XRSystem]",
|
||||
"product−Gecko",
|
||||
"keyboard−[object Keyboard]",
|
||||
"gpu−[object GPU]",
|
||||
"getInstalledRelatedApps−function getInstalledRelatedApps() { [native code] }",
|
||||
"webkitPersistentStorage−[object DeprecatedStorageQuota]",
|
||||
"doNotTrack",
|
||||
"clearAppBadge−function clearAppBadge() { [native code] }",
|
||||
"presentation−[object Presentation]",
|
||||
"serial−[object Serial]",
|
||||
"locks−[object LockManager]",
|
||||
"requestMIDIAccess−function requestMIDIAccess() { [native code] }",
|
||||
"locks−[object LockManager]",
|
||||
"requestMediaKeySystemAccess−function requestMediaKeySystemAccess() { [native code] }",
|
||||
"vendor−Google Inc.",
|
||||
"pdfViewerEnabled−true",
|
||||
"language−zh-CN",
|
||||
"setAppBadge−function setAppBadge() { [native code] }",
|
||||
"geolocation−[object Geolocation]",
|
||||
"userAgentData−[object NavigatorUAData]",
|
||||
"mediaCapabilities−[object MediaCapabilities]",
|
||||
"requestMIDIAccess−function requestMIDIAccess() { [native code] }",
|
||||
"getUserMedia−function getUserMedia() { [native code] }",
|
||||
"mediaDevices−[object MediaDevices]",
|
||||
"webkitPersistentStorage−[object DeprecatedStorageQuota]",
|
||||
"sendBeacon−function sendBeacon() { [native code] }",
|
||||
"hardwareConcurrency−32",
|
||||
"credentials−[object CredentialsContainer]",
|
||||
"storage−[object StorageManager]",
|
||||
"cookieEnabled−true",
|
||||
"pdfViewerEnabled−true",
|
||||
"windowControlsOverlay−[object WindowControlsOverlay]",
|
||||
"scheduling−[object Scheduling]",
|
||||
"pdfViewerEnabled−true",
|
||||
"hardwareConcurrency−32",
|
||||
"xr−[object XRSystem]",
|
||||
"webdriver−false",
|
||||
"getInstalledRelatedApps−function getInstalledRelatedApps() { [native code] }",
|
||||
"getInstalledRelatedApps−function getInstalledRelatedApps() { [native code] }",
|
||||
"bluetooth−[object Bluetooth]"
|
||||
]
|
||||
|
||||
document_key = ['_reactListeningo743lnnpvdg', 'location']
|
||||
|
||||
window_key = [
|
||||
"0",
|
||||
"window",
|
||||
"self",
|
||||
"document",
|
||||
"name",
|
||||
"location",
|
||||
"customElements",
|
||||
"history",
|
||||
"navigation",
|
||||
"locationbar",
|
||||
"menubar",
|
||||
"personalbar",
|
||||
"scrollbars",
|
||||
"statusbar",
|
||||
"toolbar",
|
||||
"status",
|
||||
"closed",
|
||||
"frames",
|
||||
"length",
|
||||
"top",
|
||||
"opener",
|
||||
"parent",
|
||||
"frameElement",
|
||||
"navigator",
|
||||
"origin",
|
||||
"external",
|
||||
"screen",
|
||||
"innerWidth",
|
||||
"innerHeight",
|
||||
"scrollX",
|
||||
"pageXOffset",
|
||||
"scrollY",
|
||||
"pageYOffset",
|
||||
"visualViewport",
|
||||
"screenX",
|
||||
"screenY",
|
||||
"outerWidth",
|
||||
"outerHeight",
|
||||
"devicePixelRatio",
|
||||
"clientInformation",
|
||||
"screenLeft",
|
||||
"screenTop",
|
||||
"styleMedia",
|
||||
"onsearch",
|
||||
"isSecureContext",
|
||||
"trustedTypes",
|
||||
"performance",
|
||||
"onappinstalled",
|
||||
"onbeforeinstallprompt",
|
||||
"crypto",
|
||||
"indexedDB",
|
||||
"sessionStorage",
|
||||
"localStorage",
|
||||
"onbeforexrselect",
|
||||
"onabort",
|
||||
"onbeforeinput",
|
||||
"onbeforematch",
|
||||
"onbeforetoggle",
|
||||
"onblur",
|
||||
"oncancel",
|
||||
"oncanplay",
|
||||
"oncanplaythrough",
|
||||
"onchange",
|
||||
"onclick",
|
||||
"onclose",
|
||||
"oncontentvisibilityautostatechange",
|
||||
"oncontextlost",
|
||||
"oncontextmenu",
|
||||
"oncontextrestored",
|
||||
"oncuechange",
|
||||
"ondblclick",
|
||||
"ondrag",
|
||||
"ondragend",
|
||||
"ondragenter",
|
||||
"ondragleave",
|
||||
"ondragover",
|
||||
"ondragstart",
|
||||
"ondrop",
|
||||
"ondurationchange",
|
||||
"onemptied",
|
||||
"onended",
|
||||
"onerror",
|
||||
"onfocus",
|
||||
"onformdata",
|
||||
"oninput",
|
||||
"oninvalid",
|
||||
"onkeydown",
|
||||
"onkeypress",
|
||||
"onkeyup",
|
||||
"onload",
|
||||
"onloadeddata",
|
||||
"onloadedmetadata",
|
||||
"onloadstart",
|
||||
"onmousedown",
|
||||
"onmouseenter",
|
||||
"onmouseleave",
|
||||
"onmousemove",
|
||||
"onmouseout",
|
||||
"onmouseover",
|
||||
"onmouseup",
|
||||
"onmousewheel",
|
||||
"onpause",
|
||||
"onplay",
|
||||
"onplaying",
|
||||
"onprogress",
|
||||
"onratechange",
|
||||
"onreset",
|
||||
"onresize",
|
||||
"onscroll",
|
||||
"onsecuritypolicyviolation",
|
||||
"onseeked",
|
||||
"onseeking",
|
||||
"onselect",
|
||||
"onslotchange",
|
||||
"onstalled",
|
||||
"onsubmit",
|
||||
"onsuspend",
|
||||
"ontimeupdate",
|
||||
"ontoggle",
|
||||
"onvolumechange",
|
||||
"onwaiting",
|
||||
"onwebkitanimationend",
|
||||
"onwebkitanimationiteration",
|
||||
"onwebkitanimationstart",
|
||||
"onwebkittransitionend",
|
||||
"onwheel",
|
||||
"onauxclick",
|
||||
"ongotpointercapture",
|
||||
"onlostpointercapture",
|
||||
"onpointerdown",
|
||||
"onpointermove",
|
||||
"onpointerrawupdate",
|
||||
"onpointerup",
|
||||
"onpointercancel",
|
||||
"onpointerover",
|
||||
"onpointerout",
|
||||
"onpointerenter",
|
||||
"onpointerleave",
|
||||
"onselectstart",
|
||||
"onselectionchange",
|
||||
"onanimationend",
|
||||
"onanimationiteration",
|
||||
"onanimationstart",
|
||||
"ontransitionrun",
|
||||
"ontransitionstart",
|
||||
"ontransitionend",
|
||||
"ontransitioncancel",
|
||||
"onafterprint",
|
||||
"onbeforeprint",
|
||||
"onbeforeunload",
|
||||
"onhashchange",
|
||||
"onlanguagechange",
|
||||
"onmessage",
|
||||
"onmessageerror",
|
||||
"onoffline",
|
||||
"ononline",
|
||||
"onpagehide",
|
||||
"onpageshow",
|
||||
"onpopstate",
|
||||
"onrejectionhandled",
|
||||
"onstorage",
|
||||
"onunhandledrejection",
|
||||
"onunload",
|
||||
"crossOriginIsolated",
|
||||
"scheduler",
|
||||
"alert",
|
||||
"atob",
|
||||
"blur",
|
||||
"btoa",
|
||||
"cancelAnimationFrame",
|
||||
"cancelIdleCallback",
|
||||
"captureEvents",
|
||||
"clearInterval",
|
||||
"clearTimeout",
|
||||
"close",
|
||||
"confirm",
|
||||
"createImageBitmap",
|
||||
"fetch",
|
||||
"find",
|
||||
"focus",
|
||||
"getComputedStyle",
|
||||
"getSelection",
|
||||
"matchMedia",
|
||||
"moveBy",
|
||||
"moveTo",
|
||||
"open",
|
||||
"postMessage",
|
||||
"print",
|
||||
"prompt",
|
||||
"queueMicrotask",
|
||||
"releaseEvents",
|
||||
"reportError",
|
||||
"requestAnimationFrame",
|
||||
"requestIdleCallback",
|
||||
"resizeBy",
|
||||
"resizeTo",
|
||||
"scroll",
|
||||
"scrollBy",
|
||||
"scrollTo",
|
||||
"setInterval",
|
||||
"setTimeout",
|
||||
"stop",
|
||||
"structuredClone",
|
||||
"webkitCancelAnimationFrame",
|
||||
"webkitRequestAnimationFrame",
|
||||
"chrome",
|
||||
"caches",
|
||||
"cookieStore",
|
||||
"ondevicemotion",
|
||||
"ondeviceorientation",
|
||||
"ondeviceorientationabsolute",
|
||||
"launchQueue",
|
||||
"documentPictureInPicture",
|
||||
"getScreenDetails",
|
||||
"queryLocalFonts",
|
||||
"showDirectoryPicker",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
"originAgentCluster",
|
||||
"onpageswap",
|
||||
"onpagereveal",
|
||||
"credentialless",
|
||||
"speechSynthesis",
|
||||
"onscrollend",
|
||||
"webkitRequestFileSystem",
|
||||
"webkitResolveLocalFileSystemURL",
|
||||
"sendMsgToSolverCS",
|
||||
"webpackChunk_N_E",
|
||||
"__next_set_public_path__",
|
||||
"next",
|
||||
"__NEXT_DATA__",
|
||||
"__SSG_MANIFEST_CB",
|
||||
"__NEXT_P",
|
||||
"_N_E",
|
||||
"regeneratorRuntime",
|
||||
"__REACT_INTL_CONTEXT__",
|
||||
"DD_RUM",
|
||||
"_",
|
||||
"filterCSS",
|
||||
"filterXSS",
|
||||
"__SEGMENT_INSPECTOR__",
|
||||
"__NEXT_PRELOADREADY",
|
||||
"Intercom",
|
||||
"__MIDDLEWARE_MATCHERS",
|
||||
"__STATSIG_SDK__",
|
||||
"__STATSIG_JS_SDK__",
|
||||
"__STATSIG_RERENDER_OVERRIDE__",
|
||||
"_oaiHandleSessionExpired",
|
||||
"__BUILD_MANIFEST",
|
||||
"__SSG_MANIFEST",
|
||||
"__intercomAssignLocation",
|
||||
"__intercomReloadLocation"
|
||||
]
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
import hashlib, json, random, time, uuid, pybase64
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from config import cores, cached_scripts, cached_dpl, navigator_key, document_key, window_key
|
||||
|
||||
|
||||
MAX_ITERATION = 500000
|
||||
|
||||
def get_parse_time():
|
||||
now = datetime.now(timezone(timedelta(hours=-5)))
|
||||
return now.strftime("%a %b %d %Y %H:%M:%") + " GMT-0500 (Eastern Standard Time)"
|
||||
|
||||
|
||||
|
||||
def get_config(user_agent):
|
||||
config = [
|
||||
random.choice([1920 + 1080, 2560 + 1440, 1920 + 1200, 2560 + 1600]),
|
||||
get_parse_time(),
|
||||
4294705152,
|
||||
0,
|
||||
user_agent,
|
||||
random.choice(cached_scripts) if cached_scripts else "",
|
||||
cached_dpl,
|
||||
"en-US",
|
||||
"en-US,es-US,en,es",
|
||||
0,
|
||||
random.choice(navigator_key),
|
||||
random.choice(document_key),
|
||||
random.choice(window_key),
|
||||
time.perf_counter() * 1000,
|
||||
str(uuid.uuid4()),
|
||||
"",
|
||||
random.choice(cores),
|
||||
time.time() * 1000 - (time.perf_counter() * 1000),
|
||||
]
|
||||
return config
|
||||
|
||||
|
||||
|
||||
def generate_answer(seed, diff, config):
|
||||
diff_len = len(diff)
|
||||
seed_encoded = seed.encode()
|
||||
static_config_part1 = (json.dumps(config[:3], separators=(',', ':'), ensure_ascii=False)[:-1] + ',').encode()
|
||||
static_config_part2 = (',' + json.dumps(config[4:9], separators=(',', ':'), ensure_ascii=False)[1:-1] + ',').encode()
|
||||
static_config_part3 = (',' + json.dumps(config[10:], separators=(',', ':'), ensure_ascii=False)[1:]).encode()
|
||||
|
||||
target_diff = bytes.fromhex(diff)
|
||||
|
||||
for i in range(MAX_ITERATION):
|
||||
dynamic_json_i = str(i).encode()
|
||||
dynamic_json_j = str(i >> 1).encode()
|
||||
final_json_bytes = static_config_part1 + dynamic_json_i + static_config_part2 + dynamic_json_j + static_config_part3
|
||||
base_encode = pybase64.b64encode(final_json_bytes)
|
||||
hash_value = hashlib.sha3_512(seed_encoded + base_encode).digest()
|
||||
if hash_value[:diff_len] <= target_diff:
|
||||
return base_encode.decode(), True
|
||||
|
||||
return "wQ8Lk5FbGpA2NcR9dShT6gYjU7VxZ4D" + pybase64.b64encode(f'"{seed}"'.encode()).decode(), False
|
||||
|
||||
|
||||
def get_requirements_token(config):
|
||||
seed = format(random.random())
|
||||
diff = "0fffff"
|
||||
solution, found = generate_answer(seed, diff, config)
|
||||
return 'gAAAAAC' + solution
|
||||
|
||||
def get_pow_token():
|
||||
config = get_config("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36")
|
||||
return get_requirements_token(config)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(get_pow_token())
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
# OpenAI Sentinel Token Generator
|
||||
|
||||
This repository provides a Python-based implementation to generate **Proof of Work (PoW) tokens** and **Sentinel tokens** for bypassing security mechanisms in OpenAI's ChatGPT and Sora private APIs. By reverse engineering the browser's token generation process, this project enables programmatic access to these APIs without requiring manual interaction or browser-based token generation.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Features](#features)
|
||||
- [How It Works](#how-it-works)
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [File Structure](#file-structure)
|
||||
- [Disclaimer](#disclaimer)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
OpenAI's private APIs, such as ChatGPT and Sora, use advanced security mechanisms like Proof of Work (PoW) and Sentinel tokens to prevent unauthorized access. These tokens are typically generated in the browser using a combination of cryptographic hashing, unique identifiers, and environmental data.
|
||||
|
||||
This project reverse-engineers the token generation process to allow developers to programmatically generate these tokens and interact with the APIs directly. This can be useful for automation, testing, or research purposes.
|
||||
|
||||
---
|
||||
## Features
|
||||
|
||||
- **Proof of Work Token Generation**: Implements a hashing-based PoW mechanism to generate valid tokens.
|
||||
- **Sentinel Token Generation**: Creates Sentinel tokens required for API authentication.
|
||||
- **Reverse-Engineered Browser Behavior**: Mimics browser-specific configurations and environmental data.
|
||||
- **Customizable**: Easily modify configurations like user agents, navigator keys, and more.
|
||||
|
||||
---
|
||||
## How It Works
|
||||
### Proof of Work (PoW) Token Generation
|
||||
The PoW token is generated by solving a computational challenge. The process involves:
|
||||
1. Generating a random seed and difficulty level.
|
||||
2. Iteratively hashing the seed with encoded configuration data until the hash meets the difficulty criteria.
|
||||
3. Returning the solution as a base64-encoded string.
|
||||
|
||||
The implementation is in [`proof_of_work.py`](proof_of_work.py), which uses cryptographic hashing (`sha3_512`) and base64 encoding to generate the token.
|
||||
|
||||
### Sentinel Token Generation
|
||||
The Sentinel token is generated by:
|
||||
1. Fetching API requirements using the PoW token.
|
||||
2. Parsing the API response to extract necessary fields like `turnstile` and `token`.
|
||||
3. Constructing a payload with the extracted data and generating the final Sentinel token.
|
||||
|
||||
The implementation is in [`sentinel_token.py`](sentinel_token.py), which handles API communication and token generation.
|
||||
|
||||
---
|
||||
## Installation
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/your-username/openai-sentinel.git
|
||||
cd openai-sentinel
|
||||
```
|
||||
2. Install the required dependencies:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
Ensure you have Python 3.8 or higher installed.
|
||||
|
||||
___
|
||||
## Usage
|
||||
### Generating a Proof of Work (PoW) Token
|
||||
To generate a PoW token, run the following command:
|
||||
```bash
|
||||
python proof_of_work.py
|
||||
```
|
||||
This will output a valid PoW token that can be used for API authentication.
|
||||
### Generating a Sentinel Token
|
||||
To generate a Sentinel token, run the following command:
|
||||
```bash
|
||||
python sentinel_token.py
|
||||
```
|
||||
This will output a valid Sentinel token that can be used for API authentication.
|
||||
|
||||
---
|
||||
## File Structure
|
||||
|
||||
```plaintext
|
||||
openai-sentinel/
|
||||
├── proof_of_work.py
|
||||
├── sentinel_token.py
|
||||
├── config.py
|
||||
├── requirements.txt
|
||||
├── README.md
|
||||
```
|
||||
### Key Files:
|
||||
- `proof_of_work.py`: Contains the implementation for generating PoW tokens.
|
||||
- `sentinel_token.py`: Contains the implementation for generating Sentinel tokens.
|
||||
- `config.py`: Configuration file for user agents, navigator keys, and other settings.
|
||||
- `requirements.txt`: Lists the required Python packages.
|
||||
- `README.md`: This documentation file.
|
||||
---
|
||||
## Disclaimer
|
||||
This project is intended for educational and research purposes only. The use of this code to bypass security mechanisms or access APIs without authorization may violate the terms of service of OpenAI or other third-party services. Use at your own risk, and ensure compliance with all applicable laws and regulations.
|
||||
```
|
||||
Please do not use this code for malicious purposes or to infringe on the rights of others. The author and contributors are not responsible for any misuse or damage caused by the use of this code.
|
||||
```
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
requests
|
||||
pybase64
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
import requests, json
|
||||
from uuid import uuid4
|
||||
from proof_of_work import get_pow_token
|
||||
|
||||
current_parameters = None
|
||||
|
||||
|
||||
def generate_id():
|
||||
return str(uuid4())
|
||||
|
||||
def generate_payload(data, flow):
|
||||
data['id'] = generate_id()
|
||||
data['flow'] = flow
|
||||
return json.dumps(data)
|
||||
|
||||
def fetch_requirements(flow, pow_token):
|
||||
max_retries = 3
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
response = requests.post(
|
||||
url = "https://chatgpt.com/backend-api/sentinel/req",
|
||||
data = generate_payload({'p': pow_token}, flow),
|
||||
)
|
||||
|
||||
result = response.json()
|
||||
return result, True
|
||||
except requests.exceptions.RequestException as err:
|
||||
if attempt >= 2:
|
||||
return generate_payload({'e': str(err)}, flow), False
|
||||
|
||||
def refresh_token(flow):
|
||||
pow_token = get_pow_token()
|
||||
response,_ = fetch_requirements(flow, pow_token)
|
||||
if not _:
|
||||
return response
|
||||
|
||||
try:
|
||||
|
||||
payload = generate_payload({
|
||||
'p': pow_token,
|
||||
't': response.get("turnstile", {}).get('dx', ""),
|
||||
'c': response.get('token')
|
||||
}, flow)
|
||||
return payload
|
||||
except Exception as err:
|
||||
failure = generate_payload({'e': str(err), 'p': pow_token}, flow)
|
||||
return failure
|
||||
|
||||
def get_sentinel_token():
|
||||
flow = 'sora_create_task'
|
||||
token = refresh_token(flow) #This can be used now in 'OpenAI-Sentinel-Token' header
|
||||
return token
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(get_sentinel_token())
|
||||
+19
File diff suppressed because one or more lines are too long
+15
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
||||
pub (super) mod extract_expiry;
|
||||
pub (super) mod request;
|
||||
pub (super) mod response;
|
||||
pub mod generate_sentinel_token_2;
|
||||
@@ -0,0 +1,32 @@
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use serde_derive::Serialize;
|
||||
|
||||
// TODO: Support more of these.
|
||||
const SENTINEL_FLOW: &str = "sora_init";
|
||||
const SENTINEL_FLOW_OLD: &str = "sora_create_task";
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct SentinelRequest {
|
||||
/// "Proof of Work" token
|
||||
#[serde(rename = "p")]
|
||||
pub (crate) p: String,
|
||||
|
||||
/// Random UUID
|
||||
#[serde(rename = "id")]
|
||||
pub (crate) id: String,
|
||||
|
||||
/// Frontend UI flow
|
||||
#[serde(rename = "flow")]
|
||||
pub (crate) flow: String,
|
||||
}
|
||||
|
||||
impl SentinelRequest {
|
||||
pub fn new(pow_token: String) -> Self {
|
||||
let id = generate_random_uuid();
|
||||
Self {
|
||||
p: pow_token,
|
||||
id,
|
||||
flow: SENTINEL_FLOW.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub (crate) struct SentinelResponse {
|
||||
pub (crate) token: Option<String>,
|
||||
pub (crate) turnstile: Option<SentinelTurnstile>,
|
||||
|
||||
// NB: appears to be integer seconds.
|
||||
// Not required for the sentinel token flow, but helps with renew.
|
||||
pub (crate) expire_after: Option<u32>,
|
||||
|
||||
// NB: appears to be Unix timestamp in seconds.
|
||||
// Not required for the sentinel token flow, but helps with renew.
|
||||
pub (crate) expire_at: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub (crate) struct SentinelTurnstile {
|
||||
pub (crate) dx: Option<String>,
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
use crate::constants::user_agent::USER_AGENT;
|
||||
use crate::constants::user_agent::CLIENT_USER_AGENT;
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::creds::sora_jwt_bearer_token::SoraJwtBearerToken;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
@@ -223,7 +223,7 @@ pub async fn get_image_gen_status(status_request: &StatusRequest, credentials: &
|
||||
}
|
||||
|
||||
let http_request = client.get(url.as_str())
|
||||
.header("User-Agent", USER_AGENT)
|
||||
.header("User-Agent", CLIENT_USER_AGENT)
|
||||
.header("Cookie", credentials.cookies.as_str())
|
||||
.header("Authorization", bearer_header)
|
||||
.header("Content-Type", "application/json");
|
||||
@@ -253,7 +253,7 @@ pub async fn get_image_gen_status(status_request: &StatusRequest, credentials: &
|
||||
|
||||
if !status.is_success() {
|
||||
error!("The sora status request failed; status = {:?} ; response body = {}", status, response_body);
|
||||
let error = classify_general_http_status_code_and_body(status, &response_body).await;
|
||||
let error = classify_general_http_status_code_and_body(status, &response_body);
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use crate::constants::user_agent::USER_AGENT;
|
||||
use crate::constants::user_agent::CLIENT_USER_AGENT;
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
@@ -25,7 +25,7 @@ pub async fn unimplemented_sora_job_status(
|
||||
let request = client
|
||||
.get(url)
|
||||
.header("Authorization", &authorization_header)
|
||||
.header("User-Agent", USER_AGENT)
|
||||
.header("User-Agent", CLIENT_USER_AGENT)
|
||||
.header("Cookie", &cookie);
|
||||
|
||||
let response = request.send()
|
||||
|
||||
+19
-17
@@ -1,15 +1,18 @@
|
||||
use crate::constants::user_agent::USER_AGENT;
|
||||
use crate::constants::user_agent::CLIENT_USER_AGENT;
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::error::sora_generic_api_error::SoraGenericApiError;
|
||||
use crate::requests::auth_sentinel_2::generate_sentinel_token_2::generate_sentinel_token_2;
|
||||
use crate::requests::common::task_id::TaskId;
|
||||
use crate::requests::generate_sora2_video::http_request::{HttpCreateRequest, InpaintItem};
|
||||
use crate::requests::generate_sora2_video::http_response::HttpCreateResponse;
|
||||
use crate::requests::image_gen::image_gen_http_request::{RawSoraImageGenRequest, RawSoraResponse};
|
||||
use crate::utils_internal::classify_general_http_status_code_and_body::classify_general_http_status_code_and_body;
|
||||
use log::error;
|
||||
use std::io::Write;
|
||||
use std::time::Duration;
|
||||
use wreq::header::{ACCEPT, ACCEPT_LANGUAGE, AUTHORIZATION, CONTENT_TYPE, COOKIE, ORIGIN, REFERER};
|
||||
use wreq::header::{ACCEPT, ACCEPT_LANGUAGE, AUTHORIZATION, CONTENT_TYPE, COOKIE, ORIGIN, REFERER, USER_AGENT};
|
||||
use wreq::Client;
|
||||
|
||||
const GENERATE_SORA_2_VIDEO_URL: &str = "https://sora.chatgpt.com/backend/nf/create";
|
||||
@@ -44,25 +47,26 @@ pub (crate) async fn generate_sora2_video(
|
||||
.ok_or(SoraClientError::NoBearerTokenForRequest)?
|
||||
.to_authorization_header_value();
|
||||
|
||||
//let sentinel = args.credentials.sora_sentinel.as_ref()
|
||||
// .map(|sentinel| sentinel.get_sentinel().to_string())
|
||||
// .ok_or(SoraClientError::NoSentinelTokenForRequest)?;
|
||||
let sentinel_token = args.credentials.sora_sentinel_token
|
||||
.as_ref()
|
||||
.ok_or(SoraClientError::NoSentinelTokenForRequest)?;
|
||||
|
||||
let sentinel_token = sentinel_token.to_request_header_json()?;
|
||||
|
||||
let cookie = args.credentials.cookies.to_string();
|
||||
|
||||
// TODO: Make the sec-* headers match the user agent and platform
|
||||
// TODO: No sentinel?
|
||||
// TODO: device id
|
||||
//-H 'oai-device-id: 7c216860-5b73-4e63-983f-142dbcae1809' \
|
||||
let mut http_request = client.post(GENERATE_SORA_2_VIDEO_URL)
|
||||
//.header("OpenAI-Sentinel-Token", &sentinel);
|
||||
let mut request_builder = client.post(GENERATE_SORA_2_VIDEO_URL)
|
||||
.header(ACCEPT, "*/*")
|
||||
.header("priority", "u=1, i")
|
||||
.header(REFERER, "https://sora.chatgpt.com/explore")
|
||||
.header(ORIGIN, "https://sora.chatgpt.com")
|
||||
.header(ACCEPT_LANGUAGE, "en-US,en;q=0.9")
|
||||
.header(wreq::header::USER_AGENT, USER_AGENT)
|
||||
.header(USER_AGENT, CLIENT_USER_AGENT)
|
||||
.header(COOKIE, &cookie)
|
||||
.header("OpenAI-Sentinel-Token", sentinel_token)
|
||||
.header(AUTHORIZATION, &authorization_header)
|
||||
.header(CONTENT_TYPE, "application/json")
|
||||
.header("sec-ch-ua", "\"Chromium\";v=\"140\", \"Not=A?Brand\";v=\"24\", \"Google Chrome\";v=\"140\"")
|
||||
@@ -73,7 +77,7 @@ pub (crate) async fn generate_sora2_video(
|
||||
.header("sec-fetch-site", "same-origin");
|
||||
|
||||
if let Some(timeout) = args.request_timeout {
|
||||
http_request = http_request.timeout(timeout);
|
||||
request_builder = request_builder.timeout(timeout);
|
||||
}
|
||||
|
||||
let orientation = match args.orientation {
|
||||
@@ -112,7 +116,8 @@ pub (crate) async fn generate_sora2_video(
|
||||
storyboard_id: None,
|
||||
};
|
||||
|
||||
let http_request = http_request.json(&request_body).build()
|
||||
let http_request = request_builder.json(&request_body)
|
||||
.build()
|
||||
.map_err(|err| {
|
||||
error!("Error building Sora image generation HTTP request: {:?}", err);
|
||||
SoraClientError::WreqClientError(err)
|
||||
@@ -135,11 +140,7 @@ pub (crate) async fn generate_sora2_video(
|
||||
|
||||
if !status.is_success() {
|
||||
error!("Sora image generation request returned an error (code {}) : {:?}", status.as_u16(), response_body);
|
||||
|
||||
return Err(SoraGenericApiError::UncategorizedBadResponseWithStatusAndBody {
|
||||
status_code: status,
|
||||
body: response_body.to_string(),
|
||||
}.into());
|
||||
return Err(classify_general_http_status_code_and_body(status, response_body));
|
||||
}
|
||||
|
||||
let response : HttpCreateResponse = serde_json::from_str(response_body)
|
||||
@@ -161,7 +162,8 @@ mod tests {
|
||||
pub async fn manual_test() -> AnyhowResult<()> {
|
||||
let creds = get_test_credentials()?;
|
||||
let request = GenerateSora2VideoArgs {
|
||||
prompt: "A cute corgi wearing glasses, sitting on a picnic blanket and reading a book, digital art",
|
||||
//prompt: "A cute corgi wearing glasses, sitting on a picnic blanket and reading a book, digital art",
|
||||
prompt: "A dog eating pancakes in an iHop",
|
||||
credentials: &creds,
|
||||
request_timeout: None,
|
||||
orientation: Orientation::Landscape,
|
||||
|
||||
+39
File diff suppressed because one or more lines are too long
+12
-6
@@ -1,4 +1,4 @@
|
||||
use crate::constants::user_agent::USER_AGENT;
|
||||
use crate::constants::user_agent::CLIENT_USER_AGENT;
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
@@ -7,6 +7,7 @@ use crate::error::sora_specific_api_error::SoraSpecificApiError;
|
||||
use crate::requests::common::task_id::TaskId;
|
||||
use log::{error, warn};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::io::Write;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use wreq::{Client, StatusCode};
|
||||
@@ -145,18 +146,22 @@ pub (crate) async fn image_gen_http_request(
|
||||
.ok_or(SoraClientError::NoBearerTokenForRequest)?
|
||||
.to_authorization_header_value();
|
||||
|
||||
let sentinel = credentials.sora_sentinel.as_ref()
|
||||
.map(|sentinel| sentinel.get_sentinel().to_string())
|
||||
let sentinel = credentials.sora_sentinel_token.as_ref()
|
||||
.map(|sentinel| sentinel.to_request_header_json())
|
||||
.transpose()?
|
||||
.ok_or(SoraClientError::NoSentinelTokenForRequest)?;
|
||||
|
||||
//let sentinel = credentials.sora_sentinel.as_ref()
|
||||
// .map(|sentinel| sentinel.get_sentinel().to_string())
|
||||
// .ok_or(SoraClientError::NoSentinelTokenForRequest)?;
|
||||
|
||||
let mut http_request = client.post(SORA_IMAGE_GEN_URL)
|
||||
.header("User-Agent", USER_AGENT)
|
||||
.header("User-Agent", CLIENT_USER_AGENT)
|
||||
.header("Cookie", &cookie)
|
||||
.header("Authorization", &authorization_header)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("OpenAI-Sentinel-Token", &sentinel);
|
||||
|
||||
|
||||
|
||||
if let Some(timeout) = request_timeout {
|
||||
http_request = http_request.timeout(timeout);
|
||||
}
|
||||
@@ -236,6 +241,7 @@ mod tests {
|
||||
use errors::AnyhowResult;
|
||||
|
||||
#[test]
|
||||
#[ignore] // Run manually
|
||||
fn deserialize_task_id() -> AnyhowResult<()> {
|
||||
let json = "{\"id\": \"task_foobarbaz\"}";
|
||||
let response : RawSoraResponse = serde_json::from_str(json)?;
|
||||
|
||||
+22
File diff suppressed because one or more lines are too long
+4
-18
@@ -37,33 +37,18 @@ pub async fn sora_image_gen_simple(args: SoraImageGenSimpleRequest<'_>) -> Resul
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::creds::sora_credential_builder::SoraCredentialBuilder;
|
||||
use crate::requests::image_gen::common::{ImageSize, NumImages};
|
||||
use crate::requests::image_gen::sora_image_gen_simple::{sora_image_gen_simple, SoraImageGenSimpleRequest};
|
||||
use crate::test_utils::get_test_credentials::get_test_credentials;
|
||||
use errors::AnyhowResult;
|
||||
use std::fs::read_to_string;
|
||||
use testing::test_file_path::test_file_path;
|
||||
|
||||
#[ignore] // You can manually run "ignore" tests in the IDE, but they won't run in CI.
|
||||
#[tokio::test]
|
||||
pub async fn manual_test() -> AnyhowResult<()> {
|
||||
let sentinel = read_to_string(test_file_path("test_data/temp/sentinel.txt")?)?;
|
||||
let sentinel = sentinel.trim().to_string();
|
||||
|
||||
let cookie = read_to_string(test_file_path("test_data/temp/cookie.txt")?)?;
|
||||
let cookie = cookie.trim().to_string();
|
||||
|
||||
let bearer = read_to_string(test_file_path("test_data/temp/bearer.txt")?)?;
|
||||
let bearer = bearer.trim().to_string();
|
||||
|
||||
let creds = SoraCredentialBuilder::new()
|
||||
.with_cookies(&cookie)
|
||||
.with_jwt_bearer_token(&bearer)
|
||||
.with_sora_sentinel(&sentinel)
|
||||
.build()?;
|
||||
let creds = get_test_credentials()?;
|
||||
|
||||
let response = sora_image_gen_simple(SoraImageGenSimpleRequest {
|
||||
prompt: "A pirate and a ninja fight in a battle inside a UFO. Fully photo realistic, lifelike, lens flare".to_string(),
|
||||
prompt: "A pirate ship catches on fire as an alien ufo attacks it, photorealistic".to_string(),
|
||||
num_images: NumImages::One,
|
||||
image_size: ImageSize::Square,
|
||||
credentials: &creds,
|
||||
@@ -73,6 +58,7 @@ mod tests {
|
||||
println!("task_id: {}", response.task_id);
|
||||
|
||||
assert!(response.task_id.0.starts_with("task_"));
|
||||
assert_eq!(1, 2);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
use crate::constants::user_agent::USER_AGENT;
|
||||
use crate::constants::user_agent::CLIENT_USER_AGENT;
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
@@ -102,7 +102,7 @@ pub async fn list_classic_tasks(credentials: &SoraCredentialSet) -> Result<ListT
|
||||
//}
|
||||
|
||||
let http_request = client.get(url.as_str())
|
||||
.header("User-Agent", USER_AGENT)
|
||||
.header("User-Agent", CLIENT_USER_AGENT)
|
||||
.header("Cookie", credentials.cookies.as_str())
|
||||
.header("Authorization", bearer_header)
|
||||
.header("Content-Type", "application/json");
|
||||
@@ -132,7 +132,7 @@ pub async fn list_classic_tasks(credentials: &SoraCredentialSet) -> Result<ListT
|
||||
|
||||
if !status.is_success() {
|
||||
error!("The sora task list request failed; status = {:?} ; response body = {}", status, response_body);
|
||||
let error = classify_general_http_status_code_and_body(status, &response_body).await;
|
||||
let error = classify_general_http_status_code_and_body(status, &response_body);
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use crate::constants::user_agent::USER_AGENT;
|
||||
use crate::constants::user_agent::CLIENT_USER_AGENT;
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
@@ -60,7 +60,7 @@ pub async fn list_sora2_drafts(credentials: &SoraCredentialSet) -> Result<ListSo
|
||||
.header(ACCEPT, "*/*")
|
||||
.header(REFERER, "https://sora.chatgpt.com/drafts")
|
||||
.header(ACCEPT_LANGUAGE, "en-US,en;q=0.9")
|
||||
.header(wreq::header::USER_AGENT, USER_AGENT)
|
||||
.header(wreq::header::USER_AGENT, CLIENT_USER_AGENT)
|
||||
.header(COOKIE, &cookie)
|
||||
.header(AUTHORIZATION, &authorization_header)
|
||||
.header("priority", "u=1, i")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod auth_bearer;
|
||||
pub mod auth_sentinel;
|
||||
pub mod auth_sentinel_2;
|
||||
pub mod common;
|
||||
pub mod deprecated;
|
||||
pub mod generate_sora2_video;
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use crate::constants::user_agent::USER_AGENT;
|
||||
use crate::constants::user_agent::CLIENT_USER_AGENT;
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
@@ -51,7 +51,7 @@ pub (crate) async fn upload_media_http_request(
|
||||
.header(AUTHORIZATION, &auth_header)
|
||||
.header(COOKIE, &cookie)
|
||||
.header(ORIGIN, "https://sora.chatgpt.com")
|
||||
.header("user-agent", USER_AGENT)
|
||||
.header("user-agent", CLIENT_USER_AGENT)
|
||||
.header(REFERER, "https://sora.chatgpt.com/explore")
|
||||
.header("priority", "u=1, i")
|
||||
.header("sec-ch-ua", "\"Chromium\";v=\"140\", \"Not=A?Brand\";v=\"24\", \"Google Chrome\";v=\"140\"")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::creds::sora_credential_builder::SoraCredentialBuilder;
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::creds::sora_sentinel_token::SoraSentinelToken;
|
||||
use errors::AnyhowResult;
|
||||
use std::fs::read_to_string;
|
||||
|
||||
@@ -7,8 +8,13 @@ use std::fs::read_to_string;
|
||||
pub fn get_test_credentials() -> AnyhowResult<SoraCredentialSet> {
|
||||
// TODO: This should work on other folks' machines when they exist
|
||||
// This is so brittle...
|
||||
let sentinel_token = {
|
||||
let sentinel_json = read_to_string("/Users/bt/Artcraft/credentials/sora_sentinel_token_store.json")?;
|
||||
let sentinel_json = sentinel_json.trim().to_string();
|
||||
SoraSentinelToken::from_persistent_storage_json(&sentinel_json)?
|
||||
};
|
||||
|
||||
let sentinel = read_to_string("/Users/bt/Artcraft/credentials/sora_sentinel.txt")?;
|
||||
let sentinel = sentinel.trim().to_string();
|
||||
|
||||
let cookie = read_to_string("/Users/bt/Artcraft/credentials/sora_cookies.txt")?;
|
||||
let cookie = cookie.trim().to_string();
|
||||
@@ -20,7 +26,8 @@ pub fn get_test_credentials() -> AnyhowResult<SoraCredentialSet> {
|
||||
.with_cookies(&cookie)
|
||||
.with_jwt_bearer_token(&bearer)
|
||||
.with_sora_sentinel(&sentinel)
|
||||
.with_sora_sentinel_token(&sentinel_token)
|
||||
.build()?;
|
||||
|
||||
Ok(creds)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,5 +11,5 @@ pub async fn classify_general_http_error(response: wreq::Response) -> SoraError
|
||||
Err(err) => return SoraGenericApiError::WreqError(err).into(),
|
||||
};
|
||||
|
||||
classify_general_http_status_code_and_body(status, &message).await
|
||||
classify_general_http_status_code_and_body(status, &message)
|
||||
}
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ Message: {
|
||||
|
||||
/// Classify the type of error that occurred.
|
||||
/// This first assumes the request failed and returned a non-200.
|
||||
pub async fn classify_general_http_status_code_and_body(status: StatusCode, response_body: &str) -> SoraError {
|
||||
pub fn classify_general_http_status_code_and_body(status: StatusCode, response_body: &str) -> SoraError {
|
||||
let message = response_body.to_string();
|
||||
|
||||
// TODO: I *think* this is just the bearer token, not the cookie.
|
||||
|
||||
@@ -31,7 +31,6 @@ build_metadata.workspace = true
|
||||
cookie_store.workspace = true
|
||||
enums.workspace = true
|
||||
errors.workspace = true
|
||||
fal_client.workspace = true
|
||||
filesys.workspace = true
|
||||
idempotency.workspace = true
|
||||
images.workspace = true
|
||||
@@ -43,6 +42,7 @@ primitives.workspace = true
|
||||
sqlite_tasks.workspace = true
|
||||
storyteller_client.workspace = true
|
||||
tokens.workspace = true
|
||||
url_utils.workspace = true
|
||||
web_base64.workspace = true
|
||||
|
||||
# External
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::core::artcraft_error::ArtcraftError;
|
||||
use base64::DecodeError;
|
||||
use errors::AnyhowError;
|
||||
use fal_client::error::fal_error_plus::FalErrorPlus;
|
||||
use midjourney_client::error::midjourney_error::MidjourneyError;
|
||||
use openai_sora_client::error::sora_error::SoraError;
|
||||
use storyteller_client::error::storyteller_error::StorytellerError;
|
||||
//use fal_client::error::fal_error_plus::FalErrorPlus;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GenerateError {
|
||||
@@ -14,6 +14,9 @@ pub enum GenerateError {
|
||||
|
||||
/// We couldn't find a provider to dispatch the request to.
|
||||
NoProviderAvailable,
|
||||
|
||||
/// We pulled out Fal (for now) - it's impacting build speeds. We'll add it in the future.
|
||||
FalNoLongerSupported,
|
||||
|
||||
/// There was a billing, credits, or payments issue.
|
||||
BillingIssue(BillingIssueReason),
|
||||
@@ -73,7 +76,7 @@ pub enum BillingProvider {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ProviderFailureReason {
|
||||
Fal(FalErrorPlus),
|
||||
//Fal(FalErrorPlus),
|
||||
MidjourneyError(MidjourneyError),
|
||||
/// NB: The midjourney client doesn't categorize all errors, so we have to do so on our end.
|
||||
MidjourneyJobEnqueueFailed,
|
||||
@@ -141,11 +144,11 @@ impl From<ArtcraftError> for GenerateError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FalErrorPlus> for GenerateError {
|
||||
fn from(value: FalErrorPlus) -> Self {
|
||||
Self::ProviderFailure(ProviderFailureReason::Fal(value))
|
||||
}
|
||||
}
|
||||
//impl From<FalErrorPlus> for GenerateError {
|
||||
// fn from(value: FalErrorPlus) -> Self {
|
||||
// Self::ProviderFailure(ProviderFailureReason::Fal(value))
|
||||
// }
|
||||
//}
|
||||
|
||||
impl From<MidjourneyError> for GenerateError {
|
||||
fn from(value: MidjourneyError) -> Self {
|
||||
|
||||
+2
-12
@@ -7,6 +7,7 @@ use crate::core::commands::response::failure_response_wrapper::{CommandErrorResp
|
||||
use crate::core::commands::response::shorthand::{Response, ResponseOrErrorType};
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::functional_events::credits_balance_changed_event::CreditsBalanceChangedEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationServiceProvider};
|
||||
use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent;
|
||||
use crate::core::events::generation_events::generation_enqueue_success_event::GenerationEnqueueSuccessEvent;
|
||||
@@ -18,8 +19,6 @@ use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::core::state::task_database::TaskDatabase;
|
||||
use crate::core::utils::get_url_file_extension::get_url_file_extension;
|
||||
use crate::core::utils::simple_http_download::simple_http_download;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -35,12 +34,11 @@ use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sqlite_tasks::queries::create_task::{create_task, CreateTaskArgs};
|
||||
use std::time::Duration;
|
||||
use storyteller_client::error::storyteller_error::StorytellerError;
|
||||
use storyteller_client::endpoints::media_files::get_media_file::get_media_file;
|
||||
use storyteller_client::error::storyteller_error::StorytellerError;
|
||||
use storyteller_client::utils::api_host::ApiHost;
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use crate::core::events::functional_events::credits_balance_changed_event::CreditsBalanceChangedEvent;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct EnqueueImageBgRemovalCommand {
|
||||
@@ -103,8 +101,6 @@ pub async fn enqueue_image_bg_removal_command(
|
||||
provider_priority_store: State<'_, ProviderPriorityStore>,
|
||||
task_database: State<'_, TaskDatabase>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
fal_creds_manager: State<'_, FalCredentialManager>,
|
||||
fal_task_queue: State<'_, FalTaskQueue>,
|
||||
) -> ResponseOrErrorType<EnqueueImageBgRemovalSuccessResponse, EnqueueImageBgRemovalErrorType> {
|
||||
|
||||
info!("enqueue_image_bg_removal_command called");
|
||||
@@ -117,8 +113,6 @@ pub async fn enqueue_image_bg_removal_command(
|
||||
&provider_priority_store,
|
||||
&task_database,
|
||||
&storyteller_creds_manager,
|
||||
&fal_creds_manager,
|
||||
&fal_task_queue,
|
||||
).await;
|
||||
|
||||
match result {
|
||||
@@ -167,8 +161,6 @@ pub async fn handle_request(
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
task_database: &TaskDatabase,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
// TODO(bt,2025-07-07): Other model/provider routing...
|
||||
@@ -180,8 +172,6 @@ pub async fn handle_request(
|
||||
app_env_configs,
|
||||
provider_priority_store,
|
||||
storyteller_creds_manager,
|
||||
fal_creds_manager,
|
||||
fal_task_queue,
|
||||
).await?;
|
||||
|
||||
let result = success_event
|
||||
|
||||
-6
@@ -9,8 +9,6 @@ use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -24,8 +22,6 @@ pub async fn handle_generic_bg_removal(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
let priority = provider_priority_store.get_priority()?;
|
||||
@@ -57,8 +53,6 @@ pub async fn handle_generic_bg_removal(
|
||||
app,
|
||||
app_data_root,
|
||||
app_env_configs,
|
||||
fal_creds_manager,
|
||||
fal_task_queue,
|
||||
).await;
|
||||
}
|
||||
}
|
||||
|
||||
-3
@@ -9,8 +9,6 @@ use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::core::utils::save_base64_image_to_temp_dir::save_base64_image_to_temp_dir;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -23,7 +21,6 @@ use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image
|
||||
use artcraft_api_defs::generate::image::remove_image_background::RemoveImageBackgroundRequest;
|
||||
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};
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{error, info};
|
||||
use storyteller_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
|
||||
-4
@@ -3,8 +3,6 @@ use crate::core::commands::enqueue::image_bg_removal::enqueue_image_bg_removal_c
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn handle_generic_bg_removal_fal(
|
||||
@@ -12,8 +10,6 @@ pub async fn handle_generic_bg_removal_fal(
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
Err(GenerateError::NotYetImplemented("TODO: Implement fal background removal handling".to_string()))
|
||||
}
|
||||
|
||||
-14
@@ -20,8 +20,6 @@ use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::core::state::task_database::TaskDatabase;
|
||||
use crate::core::utils::get_url_file_extension::get_url_file_extension;
|
||||
use crate::core::utils::simple_http_download::simple_http_download;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -169,8 +167,6 @@ pub async fn enqueue_contextual_edit_image_command(
|
||||
provider_priority_store: State<'_, ProviderPriorityStore>,
|
||||
task_database: State<'_, TaskDatabase>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
fal_creds_manager: State<'_, FalCredentialManager>,
|
||||
fal_task_queue: State<'_, FalTaskQueue>,
|
||||
sora_creds_manager: State<'_, SoraCredentialManager>,
|
||||
sora_task_queue: State<'_, SoraTaskQueue>,
|
||||
) -> ResponseOrErrorType<EnqueueContextualEditImageSuccessResponse, EnqueueContextualEditImageErrorType> {
|
||||
@@ -186,8 +182,6 @@ pub async fn enqueue_contextual_edit_image_command(
|
||||
&provider_priority_store,
|
||||
&task_database,
|
||||
&storyteller_creds_manager,
|
||||
&fal_creds_manager,
|
||||
&fal_task_queue,
|
||||
&sora_creds_manager,
|
||||
&sora_task_queue,
|
||||
).await;
|
||||
@@ -238,8 +232,6 @@ pub async fn handle_request(
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
task_database: &TaskDatabase,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
@@ -255,8 +247,6 @@ pub async fn handle_request(
|
||||
app_env_configs,
|
||||
provider_priority_store,
|
||||
storyteller_creds_manager,
|
||||
fal_creds_manager,
|
||||
fal_task_queue,
|
||||
sora_creds_manager,
|
||||
sora_task_queue,
|
||||
).await?
|
||||
@@ -269,8 +259,6 @@ pub async fn handle_request(
|
||||
app_env_configs,
|
||||
provider_priority_store,
|
||||
storyteller_creds_manager,
|
||||
fal_creds_manager,
|
||||
fal_task_queue,
|
||||
sora_creds_manager,
|
||||
sora_task_queue,
|
||||
).await?
|
||||
@@ -283,8 +271,6 @@ pub async fn handle_request(
|
||||
app_env_configs,
|
||||
provider_priority_store,
|
||||
storyteller_creds_manager,
|
||||
fal_creds_manager,
|
||||
fal_task_queue,
|
||||
sora_creds_manager,
|
||||
sora_task_queue,
|
||||
).await?
|
||||
|
||||
-4
@@ -7,8 +7,6 @@ use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -22,8 +20,6 @@ pub async fn handle_flux_kontext_edit(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
-4
@@ -7,8 +7,6 @@ use crate::core::events::generation_events::generation_enqueue_failure_event::Ge
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -21,8 +19,6 @@ use artcraft_api_defs::generate::image::generate_flux_pro_11_text_to_image::Gene
|
||||
use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image::GenerateFluxPro11UltraTextToImageRequest;
|
||||
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};
|
||||
use fal_client::requests::webhook::image::edit::enqueue_flux_pro_kontext_max_edit_webhook::FluxProKontextMaxNumImages;
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{error, info};
|
||||
use storyteller_client::endpoints::generate::image::edit::flux_pro_kontext_max_edit_image::flux_pro_kontext_max_edit_image;
|
||||
|
||||
-4
@@ -7,8 +7,6 @@ use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -22,8 +20,6 @@ pub async fn handle_gemini_25_flash_edit(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
-2
@@ -7,8 +7,6 @@ use crate::core::events::generation_events::generation_enqueue_failure_event::Ge
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
|
||||
-4
@@ -6,8 +6,6 @@ use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -23,8 +21,6 @@ pub async fn handle_gpt_image_1_edit(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
-4
@@ -8,8 +8,6 @@ use crate::core::events::generation_events::generation_enqueue_failure_event::Ge
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -21,8 +19,6 @@ use artcraft_api_defs::generate::image::generate_flux_pro_11_text_to_image::Gene
|
||||
use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image::GenerateFluxPro11UltraTextToImageRequest;
|
||||
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};
|
||||
use fal_client::requests::webhook::image::edit::enqueue_gpt_image_1_edit_image_webhook::GptEditImageQuality;
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{error, info};
|
||||
use storyteller_client::endpoints::generate::image::edit::gpt_image_1_edit_image::gpt_image_1_edit_image;
|
||||
|
||||
-3
@@ -14,8 +14,6 @@ use crate::core::state::data_dir::trait_data_subdir::DataSubdir;
|
||||
use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::core::utils::get_url_file_extension::get_url_file_extension;
|
||||
use crate::core::utils::simple_http_download::simple_http_download;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -27,7 +25,6 @@ use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
use errors::AnyhowResult;
|
||||
use fal_client::requests::queue::image_gen::enqueue_flux_pro_11_ultra_text_to_image::{enqueue_flux_pro_11_ultra_text_to_image, FluxPro11UltraTextToImageArgs};
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{error, info, warn};
|
||||
use openai_sora_client::recipes::image_remix_with_session_auto_renew::{image_remix_with_session_auto_renew, ImageRemixAutoRenewRequest};
|
||||
|
||||
+1
-17
@@ -22,8 +22,6 @@ use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::core::state::task_database::TaskDatabase;
|
||||
use crate::core::utils::get_url_file_extension::get_url_file_extension;
|
||||
use crate::core::utils::simple_http_download::simple_http_download;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -40,8 +38,8 @@ use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sqlite_tasks::queries::create_task::{create_task, CreateTaskArgs};
|
||||
use std::time::Duration;
|
||||
use storyteller_client::error::storyteller_error::StorytellerError;
|
||||
use storyteller_client::endpoints::media_files::get_media_file::get_media_file;
|
||||
use storyteller_client::error::storyteller_error::StorytellerError;
|
||||
use storyteller_client::utils::api_host::ApiHost;
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
@@ -158,8 +156,6 @@ pub async fn enqueue_image_inpaint_command(
|
||||
provider_priority_store: State<'_, ProviderPriorityStore>,
|
||||
task_database: State<'_, TaskDatabase>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
fal_creds_manager: State<'_, FalCredentialManager>,
|
||||
fal_task_queue: State<'_, FalTaskQueue>,
|
||||
sora_creds_manager: State<'_, SoraCredentialManager>,
|
||||
sora_task_queue: State<'_, SoraTaskQueue>,
|
||||
) -> ResponseOrErrorType<EnqueueImageInpaintSuccessResponse, EnqueueInpaintImageErrorType> {
|
||||
@@ -175,8 +171,6 @@ pub async fn enqueue_image_inpaint_command(
|
||||
&provider_priority_store,
|
||||
&task_database,
|
||||
&storyteller_creds_manager,
|
||||
&fal_creds_manager,
|
||||
&fal_task_queue,
|
||||
&sora_creds_manager,
|
||||
&sora_task_queue,
|
||||
).await;
|
||||
@@ -227,8 +221,6 @@ pub async fn handle_request(
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
task_database: &TaskDatabase,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
@@ -244,8 +236,6 @@ pub async fn handle_request(
|
||||
app_env_configs,
|
||||
provider_priority_store,
|
||||
storyteller_creds_manager,
|
||||
fal_creds_manager,
|
||||
fal_task_queue,
|
||||
sora_creds_manager,
|
||||
sora_task_queue,
|
||||
).await?
|
||||
@@ -258,8 +248,6 @@ pub async fn handle_request(
|
||||
app_env_configs,
|
||||
provider_priority_store,
|
||||
storyteller_creds_manager,
|
||||
fal_creds_manager,
|
||||
fal_task_queue,
|
||||
sora_creds_manager,
|
||||
sora_task_queue,
|
||||
).await?
|
||||
@@ -273,8 +261,6 @@ pub async fn handle_request(
|
||||
app_env_configs,
|
||||
provider_priority_store,
|
||||
storyteller_creds_manager,
|
||||
fal_creds_manager,
|
||||
fal_task_queue,
|
||||
sora_creds_manager,
|
||||
sora_task_queue,
|
||||
).await?
|
||||
@@ -288,8 +274,6 @@ pub async fn handle_request(
|
||||
app_env_configs,
|
||||
provider_priority_store,
|
||||
storyteller_creds_manager,
|
||||
fal_creds_manager,
|
||||
fal_task_queue,
|
||||
sora_creds_manager,
|
||||
sora_task_queue,
|
||||
).await?
|
||||
|
||||
-4
@@ -8,8 +8,6 @@ use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -23,8 +21,6 @@ pub async fn handle_flux_dev_juggernaut_inpaint(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
-3
@@ -10,8 +10,6 @@ use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::core::utils::save_base64_image_to_temp_dir::save_base64_image_to_temp_dir;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -24,7 +22,6 @@ use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image
|
||||
use artcraft_api_defs::generate::image::inpaint::flux_dev_juggernaut_inpaint_image::{FluxDevJuggernautInpaintImageNumImages, FluxDevJuggernautInpaintImageRequest};
|
||||
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};
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use images::encoding::image_bytes_to_png_bytes::image_bytes_to_png_bytes;
|
||||
use images::encoding::image_bytes_to_png_bytes_with_dimensions::image_bytes_to_png_bytes_with_dimensions;
|
||||
|
||||
-4
@@ -8,8 +8,6 @@ use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -23,8 +21,6 @@ pub async fn handle_flux_pro_1_inpaint(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
-3
@@ -10,8 +10,6 @@ use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::core::utils::save_base64_image_to_temp_dir::save_base64_image_to_temp_dir;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -24,7 +22,6 @@ use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image
|
||||
use artcraft_api_defs::generate::image::inpaint::flux_pro_1_inpaint_image::{FluxPro1InpaintImageNumImages, FluxPro1InpaintImageRequest};
|
||||
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};
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use images::encoding::image_bytes_to_png_bytes::image_bytes_to_png_bytes;
|
||||
use images::encoding::image_bytes_to_png_bytes_with_dimensions::image_bytes_to_png_bytes_with_dimensions;
|
||||
|
||||
-4
@@ -6,8 +6,6 @@ use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -21,8 +19,6 @@ pub async fn handle_flux_pro_kontext_inpaint(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
-3
@@ -10,8 +10,6 @@ use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::core::utils::save_base64_image_to_temp_dir::save_base64_image_to_temp_dir;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -25,7 +23,6 @@ use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image
|
||||
use artcraft_api_defs::generate::image::inpaint::flux_pro_1_inpaint_image::{FluxPro1InpaintImageNumImages, FluxPro1InpaintImageRequest};
|
||||
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};
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use images::encoding::image_bytes_to_png_bytes::image_bytes_to_png_bytes;
|
||||
use images::encoding::image_bytes_to_png_bytes_with_dimensions::image_bytes_to_png_bytes_with_dimensions;
|
||||
|
||||
-4
@@ -7,8 +7,6 @@ use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -22,8 +20,6 @@ pub async fn handle_gemini_25_flash_inpaint(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
-3
@@ -10,8 +10,6 @@ use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::ProviderPriorityStore;
|
||||
use crate::core::utils::save_base64_image_to_temp_dir::save_base64_image_to_temp_dir;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
@@ -26,7 +24,6 @@ use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image
|
||||
use artcraft_api_defs::generate::image::inpaint::flux_pro_1_inpaint_image::{FluxPro1InpaintImageNumImages, FluxPro1InpaintImageRequest};
|
||||
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};
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use images::encoding::image_bytes_to_png_bytes::image_bytes_to_png_bytes;
|
||||
use images::encoding::image_bytes_to_png_bytes_with_dimensions::image_bytes_to_png_bytes_with_dimensions;
|
||||
|
||||
-11
@@ -12,11 +12,8 @@ use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::core::state::task_database::TaskDatabase;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use fal_client::requests::queue::image_gen::enqueue_flux_pro_11_ultra_text_to_image::{enqueue_flux_pro_11_ultra_text_to_image, FluxPro11UltraTextToImageArgs};
|
||||
use log::{error, info, warn};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tauri::{AppHandle, State};
|
||||
@@ -75,8 +72,6 @@ pub async fn enqueue_image_to_3d_object_command(
|
||||
app_data_root: State<'_, AppDataRoot>,
|
||||
provider_priority_store: State<'_, ProviderPriorityStore>,
|
||||
task_database: State<'_, TaskDatabase>,
|
||||
fal_creds_manager: State<'_, FalCredentialManager>,
|
||||
fal_task_queue: State<'_, FalTaskQueue>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
sora_task_queue: State<'_, SoraTaskQueue>,
|
||||
) -> Response<EnqueueImageTo3dObjectSuccessResponse, EnqueueImageTo3dObjectErrorType, ()> {
|
||||
@@ -90,9 +85,7 @@ pub async fn enqueue_image_to_3d_object_command(
|
||||
&app_data_root,
|
||||
&provider_priority_store,
|
||||
&task_database,
|
||||
&fal_creds_manager,
|
||||
&storyteller_creds_manager,
|
||||
&fal_task_queue,
|
||||
).await;
|
||||
|
||||
match result {
|
||||
@@ -162,9 +155,7 @@ pub async fn handle_request(
|
||||
app_data_root: &AppDataRoot,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
task_database: &TaskDatabase,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
let result = handle_object(
|
||||
@@ -173,9 +164,7 @@ pub async fn handle_request(
|
||||
&app_env_configs,
|
||||
&app_data_root,
|
||||
&provider_priority_store,
|
||||
&fal_creds_manager,
|
||||
&storyteller_creds_manager,
|
||||
&fal_task_queue,
|
||||
).await;
|
||||
|
||||
let success_event = match result {
|
||||
|
||||
+2
-17
@@ -1,3 +1,4 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_to_object::enqueue_image_to_3d_object_command::EnqueueImageTo3dObjectRequest;
|
||||
use crate::core::commands::enqueue::image_to_object::generic::handle_object_artcraft::handle_object_artcraft;
|
||||
use crate::core::commands::enqueue::image_to_object::generic::handle_object_fal::handle_object_fal;
|
||||
@@ -5,11 +6,8 @@ use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use tauri::AppHandle;
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
|
||||
pub async fn handle_object(
|
||||
request: EnqueueImageTo3dObjectRequest,
|
||||
@@ -17,15 +15,14 @@ pub async fn handle_object(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
app_data_root: &AppDataRoot,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
let priority = provider_priority_store.get_priority()?;
|
||||
|
||||
for provider in priority.iter() {
|
||||
match provider {
|
||||
Provider::Fal => {} // Fallthrough
|
||||
Provider::Sora => {} // Fallthrough
|
||||
Provider::Artcraft => {
|
||||
return Ok(handle_object_artcraft(
|
||||
@@ -36,18 +33,6 @@ pub async fn handle_object(
|
||||
storyteller_creds_manager
|
||||
).await?);
|
||||
}
|
||||
Provider::Fal => {
|
||||
if fal_creds_manager.has_apparent_api_token()? {
|
||||
return Ok(handle_object_fal(
|
||||
&app,
|
||||
app_env_configs,
|
||||
app_data_root,
|
||||
request,
|
||||
fal_creds_manager,
|
||||
fal_task_queue
|
||||
).await?);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -12,7 +12,6 @@ use artcraft_api_defs::generate::object::generate_hunyuan_2_0_image_to_3d::Gener
|
||||
use artcraft_api_defs::generate::object::generate_hunyuan_2_1_image_to_3d::GenerateHunyuan21ImageTo3dRequest;
|
||||
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};
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{error, info};
|
||||
use storyteller_client::endpoints::generate::object::generate_hunyuan_3d_2_0_image_to_3d::generate_hunyuan3d_2_0_image_to_3d;
|
||||
|
||||
+2
-123
@@ -1,21 +1,8 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_to_object::enqueue_image_to_3d_object_command::{EnqueueImageTo3dObjectModel, EnqueueImageTo3dObjectRequest};
|
||||
use crate::core::commands::enqueue::image_to_object::enqueue_image_to_3d_object_command::EnqueueImageTo3dObjectRequest;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
|
||||
use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent;
|
||||
use crate::core::events::generation_events::generation_enqueue_success_event::GenerationEnqueueSuccessEvent;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::utils::download_media_file_to_temp_dir::download_media_file_to_temp_dir;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use anyhow::anyhow;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
use fal_client::requests::queue::enqueue_hunyuan2_image_to_3d::{enqueue_hunyuan2_image_to_3d, Hunyuan2Args};
|
||||
use fal_client::requests::queue::image_gen::enqueue_flux_pro_11_ultra_text_to_image::{enqueue_flux_pro_11_ultra_text_to_image, FluxPro11UltraTextToImageArgs};
|
||||
use log::{error, info};
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn handle_object_fal(
|
||||
@@ -23,114 +10,6 @@ pub async fn handle_object_fal(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
app_data_root: &AppDataRoot,
|
||||
request: EnqueueImageTo3dObjectRequest,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
let api_key = match fal_creds_manager.get_key()? {
|
||||
Some(key) => key,
|
||||
None => {
|
||||
error!("No FAL API key is set. Can't perform action.");
|
||||
let event =
|
||||
GenerationEnqueueFailureEvent::no_fal_api_key(GenerationAction::ImageTo3d);
|
||||
|
||||
if let Err(err) = event.send(&app) {
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
return Err(GenerateError::needs_fal_api_key());
|
||||
},
|
||||
};
|
||||
|
||||
//let api_key = fal_creds_manager.get_key_required()
|
||||
// .map_err(|err| {
|
||||
// error!("EnqueueTextToImage FAL api key required: {:?}", err);
|
||||
// InternalObjectError::NeedsFalApiKey
|
||||
// })?;
|
||||
|
||||
let mut temp_download;
|
||||
|
||||
if let Some(media_token) = request.image_media_token {
|
||||
temp_download = download_media_file_to_temp_dir(
|
||||
app_env_configs,
|
||||
app_data_root,
|
||||
&media_token
|
||||
).await?;
|
||||
} else {
|
||||
return Err(GenerateError::required_source_image_not_provided());
|
||||
}
|
||||
|
||||
info!("Calling FAL image to 3d ...");
|
||||
|
||||
let filename = temp_download.path().to_path_buf();
|
||||
|
||||
let mut used_model = None;
|
||||
|
||||
let result = match request.model {
|
||||
None => {
|
||||
return Err(GenerateError::no_model_specified());
|
||||
}
|
||||
Some(
|
||||
EnqueueImageTo3dObjectModel::Hunyuan3d2_0 |
|
||||
EnqueueImageTo3dObjectModel::Hunyuan3d2
|
||||
) => {
|
||||
used_model = Some(GenerationModel::Hunyuan3d2_0);
|
||||
info!("enqueue Hunyuan 3D 2.0");
|
||||
enqueue_hunyuan2_image_to_3d(Hunyuan2Args {
|
||||
image_path: filename,
|
||||
api_key: &api_key,
|
||||
}).await
|
||||
}
|
||||
_ => {
|
||||
return Err(GenerateError::AnyhowError(anyhow!("Wrong model specified: {:?}", request.model)));
|
||||
}
|
||||
};
|
||||
|
||||
let mut job_id = None;
|
||||
|
||||
match result {
|
||||
Ok(enqueued) => {
|
||||
info!("Successfully enqueued text to image");
|
||||
|
||||
job_id = Some(enqueued.request_id.to_string());
|
||||
|
||||
let event = GenerationEnqueueSuccessEvent {
|
||||
action: GenerationAction::ImageTo3d,
|
||||
service: GenerationServiceProvider::Fal,
|
||||
model: None,
|
||||
};
|
||||
|
||||
if let Err(err) = event.send(app) {
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
if let Err(err) = fal_task_queue.insert(&enqueued) {
|
||||
error!("Failed to enqueue task: {:?}", err);
|
||||
return Err(GenerateError::AnyhowError(anyhow!("Failed to enqueue task: {:?}", err)));
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to enqueue image to 3d: {:?}", err);
|
||||
|
||||
let event = GenerationEnqueueFailureEvent {
|
||||
action: GenerationAction::ImageTo3d,
|
||||
service: GenerationServiceProvider::Fal,
|
||||
model: None,
|
||||
reason: None,
|
||||
};
|
||||
|
||||
if let Err(err) = event.send(app) {
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
return Err(GenerateError::from(err));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(TaskEnqueueSuccess {
|
||||
task_type: TaskType::ObjectGeneration,
|
||||
model: used_model,
|
||||
provider: GenerationProvider::Fal,
|
||||
provider_job_id: job_id,
|
||||
})
|
||||
Err(GenerateError::FalNoLongerSupported)
|
||||
}
|
||||
|
||||
-11
@@ -13,12 +13,9 @@ use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::core::state::task_database::TaskDatabase;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use fal_client::requests::queue::image_gen::enqueue_flux_pro_11_ultra_text_to_image::{enqueue_flux_pro_11_ultra_text_to_image, FluxPro11UltraTextToImageArgs};
|
||||
use log::{error, info, warn};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tauri::{AppHandle, State};
|
||||
@@ -118,8 +115,6 @@ pub async fn enqueue_image_to_video_command(
|
||||
app_data_root: State<'_, AppDataRoot>,
|
||||
provider_priority_store: State<'_, ProviderPriorityStore>,
|
||||
task_database: State<'_, TaskDatabase>,
|
||||
fal_creds_manager: State<'_, FalCredentialManager>,
|
||||
fal_task_queue: State<'_, FalTaskQueue>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
sora_task_queue: State<'_, SoraTaskQueue>,
|
||||
sora_creds_manager: State<'_, SoraCredentialManager>,
|
||||
@@ -134,10 +129,8 @@ pub async fn enqueue_image_to_video_command(
|
||||
&app_data_root,
|
||||
&provider_priority_store,
|
||||
&task_database,
|
||||
&fal_creds_manager,
|
||||
&sora_creds_manager,
|
||||
&storyteller_creds_manager,
|
||||
&fal_task_queue,
|
||||
).await;
|
||||
|
||||
match result {
|
||||
@@ -207,10 +200,8 @@ pub async fn handle_request(
|
||||
app_data_root: &AppDataRoot,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
task_database: &TaskDatabase,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
let result = match request.model {
|
||||
@@ -231,9 +222,7 @@ pub async fn handle_request(
|
||||
app_env_configs,
|
||||
app_data_root,
|
||||
provider_priority_store,
|
||||
fal_creds_manager,
|
||||
storyteller_creds_manager,
|
||||
fal_task_queue,
|
||||
).await
|
||||
}
|
||||
};
|
||||
|
||||
+1
-17
@@ -1,13 +1,10 @@
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_to_video::enqueue_image_to_video_command::EnqueueImageToVideoRequest;
|
||||
use crate::core::commands::enqueue::image_to_video::generic::handle_video_artcraft::handle_video_artcraft;
|
||||
use crate::core::commands::enqueue::image_to_video::generic::handle_video_fal::handle_video_fal;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use tauri::AppHandle;
|
||||
|
||||
@@ -17,15 +14,14 @@ pub async fn handle_video(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
app_data_root: &AppDataRoot,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
let priority = provider_priority_store.get_priority()?;
|
||||
|
||||
for provider in priority.iter() {
|
||||
match provider {
|
||||
Provider::Fal => {} // Fallthrough
|
||||
Provider::Sora => {} // Fallthrough
|
||||
Provider::Artcraft => {
|
||||
return Ok(handle_video_artcraft(
|
||||
@@ -36,18 +32,6 @@ pub async fn handle_video(
|
||||
storyteller_creds_manager
|
||||
).await?);
|
||||
}
|
||||
Provider::Fal => {
|
||||
if fal_creds_manager.has_apparent_api_token()? {
|
||||
return Ok(handle_video_fal(
|
||||
&app,
|
||||
app_env_configs,
|
||||
app_data_root,
|
||||
request,
|
||||
fal_creds_manager,
|
||||
fal_task_queue
|
||||
).await?);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-3
@@ -18,9 +18,6 @@ use artcraft_api_defs::generate::video::generate_veo_3_fast_image_to_video::Gene
|
||||
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};
|
||||
use fal_client::requests::queue::video_gen::enqueue_kling_16_pro_image_to_video::Kling16ProAspectRatio;
|
||||
use fal_client::requests::webhook::video::enqueue_veo_2_image_to_video_webhook::Veo2AspectRatio;
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{error, info};
|
||||
use storyteller_client::endpoints::generate::video::generate_kling_16_pro_image_to_video::generate_kling_16_pro_image_to_video;
|
||||
|
||||
+3
-112
@@ -1,21 +1,8 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_to_video::enqueue_image_to_video_command::{EnqueueImageToVideoRequest, VideoModel};
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::image_to_video::enqueue_image_to_video_command::EnqueueImageToVideoRequest;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
|
||||
use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent;
|
||||
use crate::core::events::generation_events::generation_enqueue_success_event::GenerationEnqueueSuccessEvent;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::utils::download_media_file_to_temp_dir::download_media_file_to_temp_dir;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use anyhow::anyhow;
|
||||
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};
|
||||
use fal_client::requests::queue::video_gen::enqueue_kling_16_pro_image_to_video::{enqueue_kling_16_pro_image_to_video, Kling16ProArgs, Kling16ProAspectRatio, Kling16ProDuration};
|
||||
use log::{error, info};
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn handle_video_fal(
|
||||
@@ -23,102 +10,6 @@ pub async fn handle_video_fal(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
app_data_root: &AppDataRoot,
|
||||
request: EnqueueImageToVideoRequest,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
let api_key = match fal_creds_manager.get_key()? {
|
||||
Some(key) => key,
|
||||
None => {
|
||||
error!("No FAL API key is set. Can't perform action.");
|
||||
let event =
|
||||
GenerationEnqueueFailureEvent::no_fal_api_key(GenerationAction::GenerateVideo);
|
||||
|
||||
if let Err(err) = event.send(&app) {
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
return Err(GenerateError::needs_fal_api_key());
|
||||
},
|
||||
};
|
||||
|
||||
//let api_key = fal_creds_manager.get_key_required()
|
||||
// .map_err(|err| {
|
||||
// error!("EnqueueTextToImage FAL api key required: {:?}", err);
|
||||
// InternalVideoError::NeedsFalApiKey
|
||||
// })?;
|
||||
|
||||
let mut temp_download;
|
||||
|
||||
if let Some(media_token) = request.image_media_token {
|
||||
temp_download = download_media_file_to_temp_dir(
|
||||
app_env_configs,
|
||||
app_data_root,
|
||||
&media_token
|
||||
).await?;
|
||||
} else {
|
||||
return Err(GenerateError::BadInput(BadInputReason::RequiredSourceImageNotProvided));
|
||||
}
|
||||
|
||||
let filename = temp_download.path().to_path_buf();
|
||||
|
||||
let mut selected_model = None;
|
||||
|
||||
let result = match request.model {
|
||||
None => {
|
||||
return Err(GenerateError::no_model_specified());
|
||||
}
|
||||
Some(VideoModel::Kling16Pro) => {
|
||||
info!("enqueue Kling 1.6 image to video with Kling API");
|
||||
selected_model = Some(GenerationModel::Kling1_6);
|
||||
enqueue_kling_16_pro_image_to_video(Kling16ProArgs {
|
||||
image_path: filename,
|
||||
api_key: &api_key,
|
||||
duration: Kling16ProDuration::Default,
|
||||
aspect_ratio: Kling16ProAspectRatio::WideSixteenNine,
|
||||
prompt: request.prompt.as_deref().unwrap_or(""),
|
||||
}).await
|
||||
}
|
||||
_ => {
|
||||
return Err(GenerateError::AnyhowError(anyhow!("Unsupported model: {:?}", request.model)));
|
||||
}
|
||||
};
|
||||
|
||||
let mut job_id = None;
|
||||
|
||||
match result {
|
||||
Ok(enqueued) => {
|
||||
info!("Successfully enqueued Kling 1.6 image to video");
|
||||
|
||||
job_id = Some(enqueued.request_id.to_string());
|
||||
|
||||
if let Err(err) = fal_task_queue.insert(&enqueued) {
|
||||
error!("Failed to enqueue task: {:?}", err);
|
||||
return Err(GenerateError::AnyhowError(anyhow!("Failed to enqueue task: {:?}", err)));
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to enqueue image to video: {:?}", err);
|
||||
|
||||
let event = GenerationEnqueueFailureEvent {
|
||||
action: GenerationAction::GenerateVideo,
|
||||
service: GenerationServiceProvider::Fal,
|
||||
model: selected_model,
|
||||
reason: None,
|
||||
};
|
||||
|
||||
if let Err(err) = event.send(app) {
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
return Err(GenerateError::from(err));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(TaskEnqueueSuccess {
|
||||
task_type: TaskType::VideoGeneration,
|
||||
model: selected_model,
|
||||
provider: GenerationProvider::Fal,
|
||||
provider_job_id: job_id,
|
||||
})
|
||||
Err(GenerateError::FalNoLongerSupported)
|
||||
}
|
||||
|
||||
-2
@@ -7,8 +7,6 @@ use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
|
||||
+1
-18
@@ -19,8 +19,6 @@ use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::core::state::task_database::TaskDatabase;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
@@ -29,7 +27,6 @@ use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_status::TaskStatus;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use fal_client::requests::queue::image_gen::enqueue_flux_pro_11_ultra_text_to_image::{enqueue_flux_pro_11_ultra_text_to_image, FluxPro11UltraTextToImageArgs};
|
||||
use log::{error, info, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlite_tasks::queries::create_task::{create_task, CreateTaskArgs};
|
||||
@@ -146,10 +143,8 @@ pub async fn enqueue_text_to_image_command(
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
provider_priority_store: State<'_, ProviderPriorityStore>,
|
||||
task_database: State<'_, TaskDatabase>,
|
||||
fal_creds_manager: State<'_, FalCredentialManager>,
|
||||
mj_creds_manager: State<'_, MidjourneyCredentialManager>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
fal_task_queue: State<'_, FalTaskQueue>,
|
||||
sora_creds_manager: State<'_, SoraCredentialManager>,
|
||||
sora_task_queue: State<'_, SoraTaskQueue>,
|
||||
) -> Response<EnqueueTextToImageSuccessResponse, EnqueueTextToImageErrorType, ()> {
|
||||
@@ -162,11 +157,9 @@ pub async fn enqueue_text_to_image_command(
|
||||
&app_data_root,
|
||||
&provider_priority_store,
|
||||
&task_database,
|
||||
&fal_creds_manager,
|
||||
&mj_creds_manager,
|
||||
&storyteller_creds_manager,
|
||||
&app_env_configs,
|
||||
&fal_task_queue,
|
||||
&sora_creds_manager,
|
||||
&sora_task_queue,
|
||||
).await;
|
||||
@@ -232,11 +225,9 @@ pub async fn handle_request(
|
||||
app_data_root: &AppDataRoot,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
task_database: &TaskDatabase,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
mj_creds_manager: &MidjourneyCredentialManager,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
@@ -246,10 +237,8 @@ pub async fn handle_request(
|
||||
&app,
|
||||
&app_data_root,
|
||||
&provider_priority_store,
|
||||
&fal_creds_manager,
|
||||
&storyteller_creds_manager,
|
||||
&app_env_configs,
|
||||
&fal_task_queue,
|
||||
&mj_creds_manager,
|
||||
&sora_creds_manager,
|
||||
&sora_task_queue,
|
||||
@@ -282,10 +271,8 @@ pub async fn dispatch_request(
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
provider_priority_store: &ProviderPriorityStore,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
mj_creds_manager: &MidjourneyCredentialManager,
|
||||
sora_creds_manager: &SoraCredentialManager,
|
||||
sora_task_queue: &SoraTaskQueue,
|
||||
@@ -336,16 +323,12 @@ pub async fn dispatch_request(
|
||||
|
||||
for provider in priority.iter() {
|
||||
match provider {
|
||||
Provider::Fal => {} // Fallthrough
|
||||
Provider::Sora => {} // Fallthrough
|
||||
Provider::Artcraft => {
|
||||
return handle_image_artcraft(
|
||||
request, &app, app_env_configs, app_data_root, storyteller_creds_manager).await;
|
||||
}
|
||||
Provider::Fal => {
|
||||
if fal_creds_manager.has_apparent_api_token()? {
|
||||
return handle_image_fal(&app, request, fal_creds_manager, fal_task_queue).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -17,7 +17,6 @@ use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
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};
|
||||
use log::{error, info};
|
||||
use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
use openai_sora_client::recipes::simple_image_gen_with_session_auto_renew::{simple_image_gen_with_session_auto_renew, SimpleImageGenAutoRenewRequest};
|
||||
|
||||
-1
@@ -15,7 +15,6 @@ use artcraft_api_defs::generate::image::edit::gemini_25_flash_edit_image::{Gemin
|
||||
use artcraft_api_defs::generate::image::generate_gpt_image_1_text_to_image::{GenerateGptImage1TextToImageImageQuality, GenerateGptImage1TextToImageImageSize, GenerateGptImage1TextToImageNumImages, GenerateGptImage1TextToImageRequest};
|
||||
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};
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{error, info};
|
||||
use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
|
||||
-1
@@ -14,7 +14,6 @@ use artcraft_api_defs::generate::image::generate_flux_pro_11_text_to_image::{Gen
|
||||
use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image::{GenerateFluxPro11UltraTextToImageAspectRatio, GenerateFluxPro11UltraTextToImageNumImages, GenerateFluxPro11UltraTextToImageRequest};
|
||||
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};
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{error, info};
|
||||
use storyteller_client::endpoints::generate::image::generate_flux_1_dev_text_to_image::generate_flux_1_dev_text_to_image;
|
||||
|
||||
+3
-122
@@ -1,130 +1,11 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, ImageModel};
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
|
||||
use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use anyhow::anyhow;
|
||||
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};
|
||||
use fal_client::requests::queue::image_gen::enqueue_recraft3_text_to_image::{enqueue_recraft3_text_to_image, Recraft3TextToImageArgs};
|
||||
use log::{error, info};
|
||||
use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::EnqueueTextToImageRequest;
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn handle_image_fal(
|
||||
app: &AppHandle,
|
||||
request: &EnqueueTextToImageRequest,
|
||||
fal_creds_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
) -> Result<TaskEnqueueSuccess, GenerateError> {
|
||||
|
||||
let api_key = match fal_creds_manager.get_key()? {
|
||||
Some(key) => key,
|
||||
None => {
|
||||
error!("No FAL API key is set. Can't perform action.");
|
||||
let event =
|
||||
GenerationEnqueueFailureEvent::no_fal_api_key(GenerationAction::GenerateImage);
|
||||
|
||||
if let Err(err) = event.send(&app) {
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
return Err(GenerateError::needs_fal_api_key());
|
||||
},
|
||||
};
|
||||
|
||||
let api_key = fal_creds_manager.get_key_required()
|
||||
.map_err(|err| {
|
||||
error!("EnqueueTextToImage FAL api key required: {:?}", err);
|
||||
GenerateError::needs_fal_api_key()
|
||||
})?;
|
||||
|
||||
let prompt = request.prompt.as_deref().unwrap_or("");
|
||||
|
||||
let model;
|
||||
|
||||
let result = match request.model {
|
||||
None => {
|
||||
return Err(GenerateError::BadInput(BadInputReason::NoModelSpecified));
|
||||
}
|
||||
Some(
|
||||
ImageModel::GptImage1 |
|
||||
ImageModel::Midjourney
|
||||
) => {
|
||||
return Err(GenerateError::AnyhowError(anyhow!("wrong logic: another branch should handle this: {:?}", request.model)));
|
||||
}
|
||||
Some(
|
||||
ImageModel::Gemini25Flash |
|
||||
ImageModel::Flux1Dev |
|
||||
ImageModel::Flux1Schnell |
|
||||
ImageModel::FluxPro11
|
||||
) => {
|
||||
return Err(GenerateError::NotYetImplemented(format!("not yet implemented: {:?}", request.model)));
|
||||
}
|
||||
Some(ImageModel::FluxPro11Ultra) => {
|
||||
model = GenerationModel::FluxPro11Ultra;
|
||||
info!("enqueue Flux Pro 1.1 Ultra text-to-image with prompt: {}", prompt);
|
||||
enqueue_flux_pro_11_ultra_text_to_image(FluxPro11UltraTextToImageArgs {
|
||||
prompt,
|
||||
api_key: &api_key,
|
||||
}).await
|
||||
}
|
||||
Some(ImageModel::Recraft3) => {
|
||||
model = GenerationModel::Recraft3;
|
||||
info!("enqueue Recraft v3 text-to-image with prompt: {}", prompt);
|
||||
enqueue_recraft3_text_to_image(Recraft3TextToImageArgs {
|
||||
prompt,
|
||||
api_key: &api_key,
|
||||
}).await
|
||||
}
|
||||
};
|
||||
|
||||
let success_result = match result {
|
||||
Ok(enqueued) => {
|
||||
info!("Successfully enqueued text to image");
|
||||
|
||||
//let event = GenerationEnqueueSuccessEvent {
|
||||
// action: GenerationAction::GenerateImage,
|
||||
// service: GenerationServiceProvider::Fal,
|
||||
// model: None,
|
||||
//};
|
||||
|
||||
//if let Err(err) = event.send(app) {
|
||||
// error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
//}
|
||||
|
||||
if let Err(err) = fal_task_queue.insert(&enqueued) {
|
||||
error!("Failed to enqueue task: {:?}", err);
|
||||
return Err(GenerateError::AnyhowError(anyhow!("Failed to enqueue task: {:?}", err)));
|
||||
}
|
||||
|
||||
enqueued
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to enqueue text to image: {:?}", err);
|
||||
|
||||
let event = GenerationEnqueueFailureEvent {
|
||||
action: GenerationAction::GenerateImage,
|
||||
service: GenerationServiceProvider::Fal,
|
||||
model: None,
|
||||
reason: None,
|
||||
};
|
||||
|
||||
if let Err(err) = event.send(app) {
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
return Err(GenerateError::from(err));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(TaskEnqueueSuccess {
|
||||
provider: GenerationProvider::Fal,
|
||||
task_type: TaskType::ImageGeneration,
|
||||
model: Some(model),
|
||||
provider_job_id: Some(success_result.request_id.to_string()),
|
||||
})
|
||||
Err(GenerateError::FalNoLongerSupported)
|
||||
}
|
||||
|
||||
-1
@@ -16,7 +16,6 @@ use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
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};
|
||||
use log::{error, info};
|
||||
use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
use openai_sora_client::recipes::simple_image_gen_with_session_auto_renew::{simple_image_gen_with_session_auto_renew, SimpleImageGenAutoRenewRequest};
|
||||
|
||||
-1
@@ -14,7 +14,6 @@ use crate::services::storyteller::state::storyteller_credential_manager::Storyte
|
||||
use artcraft_api_defs::generate::image::generate_gpt_image_1_text_to_image::{GenerateGptImage1TextToImageImageQuality, GenerateGptImage1TextToImageImageSize, GenerateGptImage1TextToImageNumImages, GenerateGptImage1TextToImageRequest};
|
||||
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};
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{error, info};
|
||||
use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
|
||||
-1
@@ -10,7 +10,6 @@ use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
use errors::AnyhowResult;
|
||||
use fal_client::requests::queue::image_gen::enqueue_flux_pro_11_ultra_text_to_image::{enqueue_flux_pro_11_ultra_text_to_image, FluxPro11UltraTextToImageArgs};
|
||||
use log::{error, info};
|
||||
use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
use openai_sora_client::recipes::simple_image_gen_with_session_auto_renew::{simple_image_gen_with_session_auto_renew, SimpleImageGenAutoRenewRequest};
|
||||
|
||||
-1
@@ -18,7 +18,6 @@ use crate::services::storyteller::state::storyteller_credential_manager::Storyte
|
||||
use artcraft_api_defs::generate::image::generate_gpt_image_1_text_to_image::{GenerateGptImage1TextToImageImageQuality, GenerateGptImage1TextToImageImageSize, GenerateGptImage1TextToImageNumImages, GenerateGptImage1TextToImageRequest};
|
||||
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};
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use log::{error, info};
|
||||
use midjourney_client::client::midjourney_hostname::MidjourneyHostname;
|
||||
|
||||
@@ -5,8 +5,6 @@ use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::core::state::task_database::TaskDatabase;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use errors::AnyhowResult;
|
||||
|
||||
@@ -5,8 +5,6 @@ use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::core::state::task_database::TaskDatabase;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use chrono::{DateTime, Utc};
|
||||
@@ -20,6 +18,8 @@ use serde_derive::{Deserialize, Serialize};
|
||||
use sqlite_tasks::queries::list_tasks_for_frontend::list_tasks_for_frontend;
|
||||
use storyteller_client::endpoints::media_files::delete_media_file::delete_media_file;
|
||||
use tauri::{AppHandle, State};
|
||||
use enums::tauri::tasks::task_media_file_class::TaskMediaFileClass;
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::sqlite::tasks::TaskId;
|
||||
|
||||
@@ -36,11 +36,33 @@ pub struct TaskQueueItem {
|
||||
pub model_type: Option<TaskModelType>,
|
||||
pub provider: Option<GenerationProvider>,
|
||||
pub provider_job_id: Option<String>,
|
||||
pub completed_item: Option<CompletedItemData>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub completed_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct CompletedItemData {
|
||||
pub primary_media_file: MediaFileData,
|
||||
|
||||
/// The type of file(s) generated.
|
||||
pub media_file_class: Option<TaskMediaFileClass>,
|
||||
|
||||
/// If generated in a batch, we probably have a batch token we can query.
|
||||
pub maybe_batch_token: Option<BatchGenerationToken>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct MediaFileData {
|
||||
pub token: MediaFileToken,
|
||||
pub cdn_url: String,
|
||||
pub maybe_thumbnail_url_template: Option<String>,
|
||||
|
||||
// NB: The frontend wants this.
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl SerializeMarker for GetTaskQueueCommandResponse {}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -76,17 +98,45 @@ pub async fn handle_request(
|
||||
let tasks = list_tasks_for_frontend(task_database.get_connection())
|
||||
.await?;
|
||||
|
||||
Ok(tasks.tasks.into_iter()
|
||||
.map(|task| TaskQueueItem {
|
||||
id: task.id,
|
||||
task_status: task.status,
|
||||
task_type: task.task_type,
|
||||
model_type: task.model_type,
|
||||
provider: task.provider,
|
||||
provider_job_id: task.provider_job_id,
|
||||
created_at: task.created_at,
|
||||
updated_at: task.updated_at,
|
||||
completed_at: task.completed_at,
|
||||
})
|
||||
.collect())
|
||||
let mut transformed_tasks = Vec::with_capacity(tasks.tasks.len());
|
||||
|
||||
for task in tasks.tasks.into_iter() {
|
||||
let mut completed_item = None;
|
||||
|
||||
if task.status == TaskStatus::CompleteSuccess {
|
||||
let token_and_url = task.on_complete_primary_media_file_token
|
||||
.zip(task.on_complete_primary_media_file_cdn_url);
|
||||
|
||||
if let Some((primary_media_file_token, media_file_url)) = token_and_url {
|
||||
completed_item = Some(CompletedItemData {
|
||||
primary_media_file: MediaFileData {
|
||||
token: primary_media_file_token,
|
||||
cdn_url: media_file_url.clone(),
|
||||
maybe_thumbnail_url_template: task.on_complete_primary_media_file_thumbnail_url_template.clone(),
|
||||
// NB: This isn't the exact completion date. Also, fallback to now if missing.
|
||||
created_at: task.completed_at.unwrap_or_else(Utc::now),
|
||||
},
|
||||
media_file_class: task.on_complete_primary_media_file_class,
|
||||
maybe_batch_token: task.on_complete_batch_token,
|
||||
});
|
||||
} else {
|
||||
warn!("Task {} is marked complete but has no primary media file token or URL.", task.id);
|
||||
}
|
||||
}
|
||||
|
||||
transformed_tasks.push(TaskQueueItem {
|
||||
id: task.id,
|
||||
task_status: task.status,
|
||||
task_type: task.task_type,
|
||||
model_type: task.model_type,
|
||||
provider: task.provider,
|
||||
provider_job_id: task.provider_job_id,
|
||||
created_at: task.created_at,
|
||||
updated_at: task.updated_at,
|
||||
completed_at: task.completed_at,
|
||||
completed_item,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(transformed_tasks)
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::{Provider, ProviderPriorityStore};
|
||||
use crate::core::state::task_database::TaskDatabase;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
@@ -3,15 +3,12 @@ use crate::core::lifecycle::startup::tasks::initially_size_and_position_windows:
|
||||
use crate::core::lifecycle::startup::tasks::load_provider_priority_state::load_provider_priority_state;
|
||||
use crate::core::lifecycle::startup::tasks::set_app_log_level::set_app_log_level;
|
||||
use crate::core::lifecycle::startup::tasks::spawn_discord_presence_thread::spawn_discord_presence_thread;
|
||||
use crate::core::lifecycle::startup::tasks::spawn_fal_task_polling_thread::spawn_fal_task_polling_thread;
|
||||
use crate::core::lifecycle::startup::tasks::spawn_main_window_thread::spawn_main_window_thread;
|
||||
use crate::core::lifecycle::startup::tasks::spawn_sora_task_polling_thread::spawn_sora_task_polling_thread;
|
||||
use crate::core::lifecycle::startup::tasks::spawn_storyteller_threads::spawn_storyteller_threads;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::artcraft_platform_info::ArtcraftPlatformInfo;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::midjourney::threads::midjourney_long_polling_thread::midjourney_long_polling_thread;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
@@ -28,8 +25,6 @@ pub async fn handle_tauri_startup(
|
||||
storyteller_creds_manager: StorytellerCredentialManager,
|
||||
sora_credential_manager: SoraCredentialManager,
|
||||
sora_task_queue: SoraTaskQueue,
|
||||
fal_credential_manager: FalCredentialManager,
|
||||
fal_task_queue: FalTaskQueue,
|
||||
mj_creds_manager: MidjourneyCredentialManager,
|
||||
) -> AnyhowResult<()> {
|
||||
|
||||
@@ -70,15 +65,6 @@ pub async fn handle_tauri_startup(
|
||||
&sora_task_queue,
|
||||
)?;
|
||||
|
||||
spawn_fal_task_polling_thread(
|
||||
&app,
|
||||
&root,
|
||||
&app_env_configs,
|
||||
&fal_credential_manager,
|
||||
&fal_task_queue,
|
||||
&storyteller_creds_manager,
|
||||
)?;
|
||||
|
||||
tauri::async_runtime::spawn(midjourney_long_polling_thread(
|
||||
app.clone(),
|
||||
app_env_configs.clone(),
|
||||
|
||||
@@ -2,7 +2,6 @@ pub (super) mod bootstrap_task_database;
|
||||
pub (super) mod set_app_log_level;
|
||||
pub (super) mod initially_size_and_position_windows;
|
||||
pub (super) mod spawn_discord_presence_thread;
|
||||
pub (super) mod spawn_fal_task_polling_thread;
|
||||
pub (super) mod spawn_main_window_thread;
|
||||
pub (super) mod spawn_sora_task_polling_thread;
|
||||
pub (super) mod spawn_storyteller_threads;
|
||||
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::fal::state::fal_credential_manager::FalCredentialManager;
|
||||
use crate::services::fal::state::fal_task_queue::FalTaskQueue;
|
||||
use crate::services::fal::threads::fal_task_polling_thread::fal_task_polling_thread;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use errors::AnyhowResult;
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
pub fn spawn_fal_task_polling_thread(
|
||||
app: &AppHandle,
|
||||
root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
fal_credential_manager: &FalCredentialManager,
|
||||
fal_task_queue: &FalTaskQueue,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> AnyhowResult<()> {
|
||||
|
||||
tauri::async_runtime::spawn(fal_task_polling_thread(
|
||||
app.clone(),
|
||||
app_env_configs.clone(),
|
||||
root.clone(),
|
||||
fal_credential_manager.clone(),
|
||||
fal_task_queue.clone(),
|
||||
storyteller_creds_manager.clone(),
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -29,10 +29,15 @@ impl AppCredentialsDir {
|
||||
self.path.join("sora_bearer_token.txt")
|
||||
}
|
||||
|
||||
pub fn get_sora_sentinel_file_path(&self) -> PathBuf {
|
||||
#[deprecated(note="use the new sentinel token methods")]
|
||||
pub fn get_sora_legacy_sentinel_file_path(&self) -> PathBuf {
|
||||
self.path.join("sora_sentinel.txt")
|
||||
}
|
||||
|
||||
pub fn get_sora_sentinel_token_file_path(&self) -> PathBuf {
|
||||
self.path.join("sora_sentinel_token_store.json")
|
||||
}
|
||||
|
||||
pub fn get_storyteller_avt_cookie_file_path(&self) -> PathBuf {
|
||||
self.path.join("artcraft_avt.txt")
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user