From a32905da067214eedaca6fbc62573a431416c414 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Tue, 6 May 2025 22:52:16 -0400 Subject: [PATCH] launch login thread with command instead --- .../commands/sora/open_sora_login_command.rs | 60 ++++------------ crates/desktop/artcraft/src/lib.rs | 10 +-- .../persist_storyteller_cookies_task.rs | 1 - crates/desktop/artcraft/src/threads/mod.rs | 2 +- .../src/threads/sora_login_thread/mod.rs | 1 + .../sora_login_thread.rs} | 31 +++++---- crates/desktop/artcraft/src/windows/mod.rs | 1 + .../src/windows/sora_login_window/mod.rs | 1 + .../open_sora_login_window.rs | 69 +++++++++++++++++++ .../src/requests/list_media/list_media.rs | 7 +- .../src/utils/classify_general_http_error.rs | 3 +- .../src/utils/lightweight_sora_jwt_parse.rs | 6 +- 12 files changed, 119 insertions(+), 73 deletions(-) create mode 100644 crates/desktop/artcraft/src/threads/sora_login_thread/mod.rs rename crates/desktop/artcraft/src/threads/{sora_session_login_thread.rs => sora_login_thread/sora_login_thread.rs} (91%) create mode 100644 crates/desktop/artcraft/src/windows/mod.rs create mode 100644 crates/desktop/artcraft/src/windows/sora_login_window/mod.rs create mode 100644 crates/desktop/artcraft/src/windows/sora_login_window/open_sora_login_window.rs diff --git a/crates/desktop/artcraft/src/commands/sora/open_sora_login_command.rs b/crates/desktop/artcraft/src/commands/sora/open_sora_login_command.rs index 49dcc41838..d60ea4192b 100644 --- a/crates/desktop/artcraft/src/commands/sora/open_sora_login_command.rs +++ b/crates/desktop/artcraft/src/commands/sora/open_sora_login_command.rs @@ -1,32 +1,21 @@ -use crate::threads::sora_session_login_thread::{LOGIN_WINDOW_NAME, SORA_LOGIN_URL}; -use crate::utils::clear_all_webview_cookies::clear_all_webview_cookies; +use crate::state::app_preferences::app_preferences_manager::AppPreferencesManager; +use crate::state::data_dir::app_data_root::AppDataRoot; +use crate::state::sora::sora_credential_manager::SoraCredentialManager; +use crate::windows::sora_login_window::open_sora_login_window::open_sora_login_window; use errors::AnyhowResult; use log::{error, info}; use once_cell::sync::Lazy; -use std::time::Duration; -use anyhow::anyhow; -use tauri::{AppHandle, Manager, Url, WebviewUrl, WebviewWindowBuilder, WindowBuilder}; -use tokio::time::sleep_until; - -pub const START_URL_STR: &str = "https://google.com"; - -pub static START_URL: Lazy = Lazy::new(|| { - Url::parse(START_URL_STR).expect("URL should parse") -}); - -pub const SORA_HOMEPAGE_URL_STR: &str = "https://sora.com/"; - -pub static SORA_HOMEPAGE_URL : Lazy = Lazy::new(|| { - Url::parse(SORA_HOMEPAGE_URL_STR).expect("URL should parse") -}); +use tauri::{AppHandle, State}; #[tauri::command] pub async fn open_sora_login_command( app: AppHandle, + app_data_root: State<'_, AppDataRoot>, + sora_creds_manager: State<'_, SoraCredentialManager>, ) -> Result { info!("open_login_command called"); - do_open_login(&app) + do_open_login(&app, &app_data_root, &sora_creds_manager) .await .map_err(|err| { error!("Error opening login: {:?}", err); @@ -36,35 +25,14 @@ pub async fn open_sora_login_command( Ok("result".to_string()) } -async fn do_open_login(app: &AppHandle) -> AnyhowResult<()> { +async fn do_open_login( + app: &AppHandle, + app_data_root: &AppDataRoot, + sora_creds_manager: &SoraCredentialManager, +) -> AnyhowResult<()> { info!("Building login window..."); - let url = WebviewUrl::External(START_URL.clone()); - - let window = WebviewWindowBuilder::new(app, LOGIN_WINDOW_NAME, url) - //.user_agent(openai_sora_client::credentials::USER_AGENT) - .always_on_top(true) - .title("Login to OpenAI") - .center() - .resizable(true) - .visible(true) - .closable(true) - .min_inner_size(200.0, 800.0) - .focused(true) - .devtools(true) - .build()?; - - let webview = window.get_webview(LOGIN_WINDOW_NAME) - .ok_or_else(|| anyhow!("no webview found"))?; - - clear_all_webview_cookies(&webview)?; - - webview.navigate(SORA_HOMEPAGE_URL.clone())?; - - // NB: We're starting to get Cloudflare protection screens. Let's try to avoid. - tokio::time::sleep(Duration::from_millis(100)).await; - - webview.navigate(SORA_LOGIN_URL.clone())?; + open_sora_login_window(app, app_data_root, sora_creds_manager).await?; info!("Done."); Ok(()) diff --git a/crates/desktop/artcraft/src/lib.rs b/crates/desktop/artcraft/src/lib.rs index 65f9c29b06..ecd812e4d0 100644 --- a/crates/desktop/artcraft/src/lib.rs +++ b/crates/desktop/artcraft/src/lib.rs @@ -3,16 +3,18 @@ pub mod events; pub mod state; pub mod threads; pub mod utils; +pub mod windows; use tauri::Manager; -use crate::commands::flip_image::flip_image; use crate::commands::app_preferences::get_app_preferences_command::get_app_preferences_command; use crate::commands::app_preferences::update_app_preference_command::update_app_preferences_command; +use crate::commands::flip_image::flip_image; use crate::commands::platform_info_command::platform_info_command; use crate::commands::sora::open_sora_login_command::open_sora_login_command; use crate::commands::sora::sora_image_generation_command::sora_image_generation_command; use crate::commands::sora::sora_image_remix_command::sora_image_remix_command; +use crate::state::app_preferences::app_preferences_manager::load_app_preferences_or_default; use crate::state::data_dir::app_data_root::AppDataRoot; use crate::state::main_window_size::MainWindowSize; use crate::state::sora::sora_credential_manager::SoraCredentialManager; @@ -20,13 +22,11 @@ use crate::state::sora::sora_task_queue::SoraTaskQueue; use crate::state::storyteller::storyteller_credential_manager::StorytellerCredentialManager; use crate::threads::discord_presence_thread::discord_presence_thread; use crate::threads::main_window_thread::main_window_thread::main_window_thread; -use crate::threads::sora_session_login_thread::sora_session_login_thread; use crate::threads::sora_task_polling_thread::sora_task_polling_thread; use crate::utils::webview_unsafe::webview_unsafe_for_app; -use crate::state::app_preferences::app_preferences_manager::load_app_preferences_or_default; -use tauri_plugin_http; use tauri_plugin_dialog; +use tauri_plugin_http; use tauri_plugin_log::Target; use tauri_plugin_log::TargetKind; @@ -108,7 +108,7 @@ pub fn run() { } } - tauri::async_runtime::spawn(sora_session_login_thread(app_2, app_data_root_2, sora_creds_manager_2)); + //tauri::async_runtime::spawn(sora_session_login_thread(app_2, app_data_root_2, sora_creds_manager_2)); tauri::async_runtime::spawn(main_window_thread(app_3, app_data_root_3, storyteller_creds_manager_2)); tauri::async_runtime::spawn(sora_task_polling_thread(app_4, app_data_root_4, sora_creds_manager_3, storyteller_creds_manager_3, sora_task_queue_2)); tauri::async_runtime::spawn(discord_presence_thread()); diff --git a/crates/desktop/artcraft/src/threads/main_window_thread/persist_storyteller_cookies_task.rs b/crates/desktop/artcraft/src/threads/main_window_thread/persist_storyteller_cookies_task.rs index 066a1f294c..49c5c4bdf0 100644 --- a/crates/desktop/artcraft/src/threads/main_window_thread/persist_storyteller_cookies_task.rs +++ b/crates/desktop/artcraft/src/threads/main_window_thread/persist_storyteller_cookies_task.rs @@ -1,7 +1,6 @@ use crate::state::app_startup_time::AppStartupTime; use crate::state::data_dir::app_data_root::AppDataRoot; use crate::state::storyteller::storyteller_credential_manager::StorytellerCredentialManager; -use crate::threads::sora_session_login_thread::LOGIN_WINDOW_NAME; use anyhow::anyhow; use chrono::TimeDelta; use errors::AnyhowResult; diff --git a/crates/desktop/artcraft/src/threads/mod.rs b/crates/desktop/artcraft/src/threads/mod.rs index 1be2c3b811..2cdf48823b 100644 --- a/crates/desktop/artcraft/src/threads/mod.rs +++ b/crates/desktop/artcraft/src/threads/mod.rs @@ -1,4 +1,4 @@ pub mod discord_presence_thread; pub mod main_window_thread; -pub mod sora_session_login_thread; +pub mod sora_login_thread; pub mod sora_task_polling_thread; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/threads/sora_login_thread/mod.rs b/crates/desktop/artcraft/src/threads/sora_login_thread/mod.rs new file mode 100644 index 0000000000..68b7fbf025 --- /dev/null +++ b/crates/desktop/artcraft/src/threads/sora_login_thread/mod.rs @@ -0,0 +1 @@ +pub mod sora_login_thread; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/threads/sora_session_login_thread.rs b/crates/desktop/artcraft/src/threads/sora_login_thread/sora_login_thread.rs similarity index 91% rename from crates/desktop/artcraft/src/threads/sora_session_login_thread.rs rename to crates/desktop/artcraft/src/threads/sora_login_thread/sora_login_thread.rs index 994f9ea032..93457000e4 100644 --- a/crates/desktop/artcraft/src/threads/sora_session_login_thread.rs +++ b/crates/desktop/artcraft/src/threads/sora_login_thread/sora_login_thread.rs @@ -33,25 +33,32 @@ pub static SORA_ROOT_URL: Lazy = Lazy::new(|| { Url::parse(SORA_ROOT_URL_STR).expect("URL should parse") }); -pub async fn sora_session_login_thread( +pub async fn sora_login_thread( app: AppHandle, app_data_root: AppDataRoot, sora_creds_manager: SoraCredentialManager ) -> ! { loop { - for (window_name, webview) in app.webviews() { - if window_name == LOGIN_WINDOW_NAME { - let result = check_login_window( - &webview, - &app_data_root, - &sora_creds_manager, - ).await; - if let Err(err) = result { - error!("Error checking login window: {:?}", err); - } - break; + info!(">>> SORA LOGIN THREAD <<<"); + + let login_webview = match app.get_webview(LOGIN_WINDOW_NAME) { + Some(webview) => webview, + None => { + tokio::time::sleep(std::time::Duration::from_millis(2_000)).await; + continue; } + }; + + let result = check_login_window( + &login_webview, + &app_data_root, + &sora_creds_manager, + ).await; + + if let Err(err) = result { + error!("Error checking login window: {:?}", err); } + tokio::time::sleep(std::time::Duration::from_millis(2_000)).await; } } diff --git a/crates/desktop/artcraft/src/windows/mod.rs b/crates/desktop/artcraft/src/windows/mod.rs new file mode 100644 index 0000000000..3843c67df6 --- /dev/null +++ b/crates/desktop/artcraft/src/windows/mod.rs @@ -0,0 +1 @@ +pub mod sora_login_window; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/windows/sora_login_window/mod.rs b/crates/desktop/artcraft/src/windows/sora_login_window/mod.rs new file mode 100644 index 0000000000..dc86251322 --- /dev/null +++ b/crates/desktop/artcraft/src/windows/sora_login_window/mod.rs @@ -0,0 +1 @@ +pub mod open_sora_login_window; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/windows/sora_login_window/open_sora_login_window.rs b/crates/desktop/artcraft/src/windows/sora_login_window/open_sora_login_window.rs new file mode 100644 index 0000000000..2e19df5876 --- /dev/null +++ b/crates/desktop/artcraft/src/windows/sora_login_window/open_sora_login_window.rs @@ -0,0 +1,69 @@ +use crate::state::data_dir::app_data_root::AppDataRoot; +use crate::state::sora::sora_credential_manager::SoraCredentialManager; +use crate::threads::sora_login_thread::sora_login_thread::{sora_login_thread, LOGIN_WINDOW_NAME, SORA_LOGIN_URL}; +use crate::utils::clear_all_webview_cookies::clear_all_webview_cookies; +use anyhow::anyhow; +use errors::AnyhowResult; +use once_cell::sync::Lazy; +use reqwest::Url; +use std::time::Duration; +use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindowBuilder}; + +pub const START_URL_STR: &str = "https://google.com"; + +pub static START_URL: Lazy = Lazy::new(|| { + Url::parse(START_URL_STR).expect("URL should parse") +}); + +pub const SORA_HOMEPAGE_URL_STR: &str = "https://sora.com/"; + +pub static SORA_HOMEPAGE_URL : Lazy = Lazy::new(|| { + Url::parse(SORA_HOMEPAGE_URL_STR).expect("URL should parse") +}); + +pub async fn open_sora_login_window( + app: &AppHandle, + app_data_root: &AppDataRoot, + sora_creds_manager: &SoraCredentialManager, +) -> AnyhowResult<()> { + if app.get_window(LOGIN_WINDOW_NAME).is_some() { + return Err(anyhow!("Login window already open")); + } + + let url = WebviewUrl::External(START_URL.clone()); + + let window = WebviewWindowBuilder::new(app, LOGIN_WINDOW_NAME, url) + //.user_agent(openai_sora_client::credentials::USER_AGENT) + .always_on_top(true) + .title("Login to OpenAI's Sora") + .center() + .resizable(true) + .visible(true) + .closable(true) + .min_inner_size(200.0, 800.0) + .focused(true) + .devtools(true) + .build()?; + + let webview = window.get_webview(LOGIN_WINDOW_NAME) + .ok_or_else(|| anyhow!("no webview found"))?; + + clear_all_webview_cookies(&webview)?; + + webview.navigate(SORA_HOMEPAGE_URL.clone())?; + + // NB: We're starting to get Cloudflare protection screens. Let's try to avoid. + tokio::time::sleep(Duration::from_millis(100)).await; + + webview.navigate(SORA_LOGIN_URL.clone())?; + + let app_handle = app.clone(); + let app_data_root = app_data_root.clone(); + let sora_creds_manager = sora_creds_manager.clone(); + + let _ = tauri::async_runtime::spawn(async move { + sora_login_thread(app_handle, app_data_root, sora_creds_manager).await; + }); + + Ok(()) +} diff --git a/crates/lib/api_clients/openai_sora_client/src/requests/list_media/list_media.rs b/crates/lib/api_clients/openai_sora_client/src/requests/list_media/list_media.rs index f52df6b4cd..00e490ff95 100644 --- a/crates/lib/api_clients/openai_sora_client/src/requests/list_media/list_media.rs +++ b/crates/lib/api_clients/openai_sora_client/src/requests/list_media/list_media.rs @@ -30,6 +30,8 @@ pub struct TaskResponse { pub height: u32, pub width: u32, pub operation: String, + + // There are many more fields [...] } /// Note: this request only requires a valid bearer token, and it doesn't require a cookie payload at all! @@ -71,10 +73,9 @@ pub async fn list_media(credentials: &SoraCredentialSet) -> Result SoraErr Err(err) => return SoraError::ReqwestError(err), }; - println!("Failed to fetch media list: {}", status); - println!("Message: {}", message); /* Old cookie - @@ -36,6 +34,7 @@ Message: { } */ + // TODO: I *think* this is just the bearer token, not the cookie. let cookie_expired = message.contains("Your authentication token has expired. Please try signing in again.") || message.contains("token_expired"); diff --git a/crates/lib/api_clients/openai_sora_client/src/utils/lightweight_sora_jwt_parse.rs b/crates/lib/api_clients/openai_sora_client/src/utils/lightweight_sora_jwt_parse.rs index fd8eb721d3..d0e82572e2 100644 --- a/crates/lib/api_clients/openai_sora_client/src/utils/lightweight_sora_jwt_parse.rs +++ b/crates/lib/api_clients/openai_sora_client/src/utils/lightweight_sora_jwt_parse.rs @@ -1,13 +1,13 @@ //! Adapted from 'jwt' crate +use crate::sora_error::SoraError; use anyhow::anyhow; -use base64::Engine; use base64::prelude::BASE64_URL_SAFE_NO_PAD; +use base64::Engine; use chrono::{DateTime, Utc}; +use errors::AnyhowResult; use log::warn; use serde_json::Value; -use errors::AnyhowResult; -use crate::sora_error::SoraError; const SEPARATOR: &str = ".";