mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
update sora error type - much more semantic
This commit is contained in:
1 parent
a96db56d8f
commit
2d3ec8b4e5
38 files changed
+498
-317
No files matched your search
@@ -1,5 +1,9 @@
|
||||
use crate::creds::sora_cookies::SoraCookies;
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::sora_error::SoraError;
|
||||
use crate::creds::sora_jwt_bearer_token::SoraJwtBearerToken;
|
||||
use crate::creds::sora_sentinel::SoraSentinel;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
|
||||
pub struct SoraCredentialBuilder {
|
||||
cookies: Option<String>,
|
||||
@@ -16,29 +20,41 @@ impl SoraCredentialBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cookies(mut self, cookies: &str) -> Self {
|
||||
// Consuming methods
|
||||
|
||||
pub fn with_cookies(mut self, cookies: &str) -> Self {
|
||||
self.cookies = Some(cookies.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn jwt_bearer_token(mut self, token: &str) -> Self {
|
||||
pub fn with_jwt_bearer_token(mut self, token: &str) -> Self {
|
||||
self.jwt_bearer_token = Some(token.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sora_sentinel(mut self, sentinel: &str) -> Self {
|
||||
pub fn with_sora_sentinel(mut self, sentinel: &str) -> Self {
|
||||
self.sora_sentinel = Some(sentinel.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
// Mutable methods
|
||||
|
||||
pub fn set_cookies(&mut self, cookies: &str) {
|
||||
self.cookies = Some(cookies.to_string());
|
||||
}
|
||||
|
||||
pub fn set_jwt_bearer_token(&mut self, token: &str) {
|
||||
self.jwt_bearer_token = Some(token.to_string());
|
||||
}
|
||||
|
||||
pub fn set_sora_sentinel(&mut self, sentinel: &str) {
|
||||
self.sora_sentinel = Some(sentinel.to_string());
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<SoraCredentialSet, SoraError> {
|
||||
use crate::creds::sora_cookies::SoraCookies;
|
||||
use crate::creds::sora_jwt_bearer_token::SoraJwtBearerToken;
|
||||
use crate::creds::sora_sentinel::SoraSentinel;
|
||||
|
||||
let cookies = match self.cookies {
|
||||
Some(c) => SoraCookies::new(c),
|
||||
None => return Err(SoraError::SoraCredentialBuilderError("no cookies provided".to_string())),
|
||||
None => return Err(SoraClientError::SoraCredentialBuilderError("no cookies provided").into()),
|
||||
};
|
||||
|
||||
let jwt_bearer_token = match self.jwt_bearer_token {
|
||||
|
||||
@@ -39,12 +39,12 @@ impl SoraCredentialSet {
|
||||
|
||||
pub fn to_builder(&self) -> SoraCredentialBuilder {
|
||||
let mut builder = SoraCredentialBuilder::new()
|
||||
.cookies(self.cookies.as_str());
|
||||
.with_cookies(self.cookies.as_str());
|
||||
if let Some(bearer) = &self.jwt_bearer_token {
|
||||
builder = builder.jwt_bearer_token(bearer.as_str());
|
||||
builder = builder.with_jwt_bearer_token(bearer.as_str());
|
||||
}
|
||||
if let Some(sentinel) = &self.sora_sentinel {
|
||||
builder = builder.sora_sentinel(sentinel.as_str());
|
||||
builder = builder.with_sora_sentinel(sentinel.as_str());
|
||||
}
|
||||
builder
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::utils::lightweight_sora_jwt_parse::{lightweight_sora_jwt_parse, SoraJwtClaims};
|
||||
use errors::AnyhowResult;
|
||||
|
||||
/// Sora bearer tokens are JWT tokens that can be minted with a valid cookie/session.
|
||||
#[derive(Clone)]
|
||||
@@ -9,7 +9,7 @@ pub struct SoraJwtBearerToken {
|
||||
}
|
||||
|
||||
impl SoraJwtBearerToken {
|
||||
pub fn new(token: String) -> AnyhowResult<Self> {
|
||||
pub fn new(token: String) -> Result<Self, SoraError> {
|
||||
let jwt_claims = lightweight_sora_jwt_parse(&token)?;
|
||||
Ok(SoraJwtBearerToken {
|
||||
token,
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod sora_client_error;
|
||||
pub mod sora_error;
|
||||
pub mod sora_generic_api_error;
|
||||
pub mod sora_specific_api_error;
|
||||
@@ -0,0 +1,49 @@
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SoraClientError {
|
||||
/// An error reading the file for upload.
|
||||
FileForUploadReadError(std::io::Error),
|
||||
|
||||
/// The file path provided for upload is invalid.
|
||||
FileForUploadHasInvalidPath,
|
||||
|
||||
/// Something is wrong with the JWT bearer token.
|
||||
/// This error originates on our end as we try to parse the JWT.
|
||||
LocalJwtClaimsParseError(String),
|
||||
|
||||
/// There was an error constructing the form-multipart request.
|
||||
MultipartFormError(wreq::Error),
|
||||
|
||||
/// We haven't received a bearer token yet
|
||||
/// This is our own internal application state error, not something Sora returns.
|
||||
/// We know our client can't make the request, so we preemptively fail it.
|
||||
NoBearerTokenAvailable,
|
||||
|
||||
/// Issue with using the SoraCredentialBuilder.
|
||||
SoraCredentialBuilderError(&'static str),
|
||||
|
||||
/// Error parsing a request URL.
|
||||
UrlParseError(url::ParseError),
|
||||
|
||||
/// An error was encountered in building the Wreq client
|
||||
WreqClientError(wreq::Error),
|
||||
}
|
||||
|
||||
impl Error for SoraClientError {}
|
||||
|
||||
impl Display for SoraClientError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::FileForUploadReadError(err) => write!(f, "Error reading file for upload: {}", err),
|
||||
Self::FileForUploadHasInvalidPath => write!(f, "The file path provided for upload is invalid."),
|
||||
Self::LocalJwtClaimsParseError(msg) => write!(f, "Local JWT claims parse error: {}", msg),
|
||||
Self::MultipartFormError(err) => write!(f, "Multipart form error: {}", err),
|
||||
Self::NoBearerTokenAvailable => write!(f, "No bearer token available. The client needs a bearer token to make the request."),
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_generic_api_error::SoraGenericApiError;
|
||||
use crate::error::sora_specific_api_error::SoraSpecificApiError;
|
||||
use cloudflare_errors::cloudflare_error::CloudflareError;
|
||||
use std::error::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SoraError {
|
||||
Client(SoraClientError),
|
||||
ApiSpecific(SoraSpecificApiError),
|
||||
ApiGeneric(SoraGenericApiError),
|
||||
}
|
||||
|
||||
impl SoraError {
|
||||
pub fn is_sora_having_downtime_issues(&self) -> bool {
|
||||
match self {
|
||||
Self::ApiGeneric(SoraGenericApiError::Http502ErrorBadGateway(_)) => true,
|
||||
Self::ApiGeneric(SoraGenericApiError::CloudflareError(CloudflareError::BadGateway502)) => true,
|
||||
Self::ApiGeneric(SoraGenericApiError::CloudflareError(CloudflareError::GatewayTimeout504)) => true,
|
||||
Self::ApiGeneric(SoraGenericApiError::CloudflareError(CloudflareError::TimeoutOccurred524)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for SoraError {}
|
||||
|
||||
impl std::fmt::Display for SoraError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Client(e) => write!(f, "SoraClientError: {:?}", e),
|
||||
Self::ApiSpecific(e) => write!(f, "SoraSpecificApiError: {:?}", e),
|
||||
Self::ApiGeneric(e) => write!(f, "SoraGenericApiError: {:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SoraClientError> for SoraError {
|
||||
fn from(error: SoraClientError) -> Self {
|
||||
Self::Client(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SoraSpecificApiError> for SoraError {
|
||||
fn from(error: SoraSpecificApiError) -> Self {
|
||||
Self::ApiSpecific(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SoraGenericApiError> for SoraError {
|
||||
fn from(error: SoraGenericApiError) -> Self {
|
||||
Self::ApiGeneric(error)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use cloudflare_errors::cloudflare_error::CloudflareError;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use wreq::StatusCode;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SoraGenericApiError {
|
||||
/// Specific Cloudflare errors.
|
||||
CloudflareError(CloudflareError),
|
||||
|
||||
/// HTTP 502 Bad Gateway
|
||||
/// Sora is having issues.
|
||||
/// Typically served from Cloudflare
|
||||
/// We preserve the message for debugging.
|
||||
Http502ErrorBadGateway(String),
|
||||
|
||||
/// serde_json::Error, likely from JSON deserialization schema mismatch.
|
||||
/// Includes the original body.
|
||||
SerdeResponseParseErrorWithBody(serde_json::Error, String),
|
||||
|
||||
/// Another error occurred.
|
||||
/// TODO: Try to get rid of this.
|
||||
UncategorizedBadResponse(String),
|
||||
|
||||
/// An uncategorized bad HTTP response from Sora.
|
||||
UncategorizedBadResponseWithStatusAndBody {
|
||||
status_code: StatusCode,
|
||||
body: String,
|
||||
},
|
||||
|
||||
/// An uncaught error from the API client.
|
||||
WreqError(wreq::Error),
|
||||
}
|
||||
|
||||
impl Error for SoraGenericApiError {}
|
||||
|
||||
impl Display for SoraGenericApiError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::CloudflareError(err) => write!(f, "Cloudflare error: {}", err),
|
||||
Self::Http502ErrorBadGateway(msg) => write!(f, "HTTP 502 Bad Gateway: {}", msg),
|
||||
Self::SerdeResponseParseErrorWithBody(err, body) => write!(f, "Failed to parse response body: {:?}. Body: {}", err, body),
|
||||
Self::UncategorizedBadResponseWithStatusAndBody { status_code, body } => write!(f, "Uncategorized bad response: status code {}, body: {}", status_code, body),
|
||||
Self::UncategorizedBadResponse(msg) => write!(f, "Uncategorized bad response status: {}", msg),
|
||||
Self::WreqError(err) => write!(f, "Wreq client error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SoraSpecificApiError {
|
||||
/// Failed to generate a bearer token despite the `200 OK` response.
|
||||
/// This has been generated by sending old/expired session cookies or empty cookies.
|
||||
FailedToGenerateBearerToken,
|
||||
|
||||
/// Sora requires users to set up an account username before they can generate images.
|
||||
/// This requires the user to set up that username.
|
||||
SoraUsernameNotYetCreated,
|
||||
|
||||
/// We're sending too many tasks to Sora.
|
||||
/// Sora is telling us to cool it.
|
||||
TooManyConcurrentTasks,
|
||||
|
||||
// TODO: Try to make concrete error types for these distinct cases.
|
||||
/// Unauthorized, cookie and/or bearer token expired. We'll need to ask for a refreshed login.
|
||||
///
|
||||
/// Example message, e.g. from the upload endpoint:
|
||||
/// {
|
||||
/// "error": {
|
||||
/// "message": "Your authentication token has expired. Please try signing in again.",
|
||||
/// "type": "invalid_request_error",
|
||||
/// "param": null,
|
||||
/// "code": "token_expired"
|
||||
/// }
|
||||
/// }
|
||||
UnauthorizedCookieOrBearerExpired,
|
||||
}
|
||||
|
||||
impl Error for SoraSpecificApiError {}
|
||||
|
||||
impl Display for SoraSpecificApiError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::FailedToGenerateBearerToken => write!(f, "Failed to generate bearer token. Probably invalid or expired session cookies."),
|
||||
Self::SoraUsernameNotYetCreated => write!(f, "Sora username not yet created. Please create a username in the Sora app or website."),
|
||||
Self::TooManyConcurrentTasks => write!(f, "Too many concurrent tasks. Please wait."),
|
||||
Self::UnauthorizedCookieOrBearerExpired => write!(f, "Unauthorized: cookie and/or bearer token expired"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
pub mod creds;
|
||||
pub mod error;
|
||||
pub mod recipes;
|
||||
pub mod requests;
|
||||
pub mod sora_error;
|
||||
pub mod utils;
|
||||
+15
-13
@@ -1,12 +1,14 @@
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::creds::sora_jwt_bearer_token::SoraJwtBearerToken;
|
||||
use crate::creds::sora_sentinel::SoraSentinel;
|
||||
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::bearer::generate_bearer_with_cookie::generate_bearer_with_cookie;
|
||||
use crate::requests::image_gen::common::{ImageSize, NumImages, SoraImageGenResponse};
|
||||
use crate::requests::image_gen::sora_image_gen_remix::{sora_image_gen_remix, SoraImageGenRemixRequest};
|
||||
use crate::requests::image_gen::{image_gen_http_request, SoraImageGenError};
|
||||
use crate::requests::sentinel_refresh::generate::token::generate_token;
|
||||
use crate::sora_error::SoraError;
|
||||
use anyhow::anyhow;
|
||||
use errors::AnyhowResult;
|
||||
use log::{info, warn};
|
||||
@@ -47,17 +49,17 @@ pub async fn image_remix_with_session_auto_renew(request: ImageRemixAutoRenewReq
|
||||
match err {
|
||||
// We'll fail these requests...
|
||||
SoraImageGenError::TooManyConcurrentTasks(_) => {
|
||||
return Err(SoraError::TooManyConcurrentTasks);
|
||||
return Err(SoraSpecificApiError::TooManyConcurrentTasks.into());
|
||||
}
|
||||
SoraImageGenError::UsernameRequired(_err) => {
|
||||
return Err(SoraSpecificApiError::SoraUsernameNotYetCreated.into())
|
||||
}
|
||||
SoraImageGenError::GenericError(err) => {
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("image remix failed with GenericError: {:?}", err)))
|
||||
return Err(SoraGenericApiError::UncategorizedBadResponse(format!("image remix failed with GenericError: {:?}", err)).into())
|
||||
}
|
||||
SoraImageGenError::NetworkError(err) => {
|
||||
// TODO: The underlying type should be a reqwest::Error.
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("network error: {:?}", err)))
|
||||
}
|
||||
SoraImageGenError::UsernameRequired(_err) => {
|
||||
return Err(SoraError::SoraUsernameNotYetCreated)
|
||||
return Err(SoraGenericApiError::UncategorizedBadResponse(format!("network error: {:?}", err)).into())
|
||||
}
|
||||
|
||||
// We'll retry these requests...
|
||||
@@ -85,12 +87,12 @@ pub async fn image_remix_with_session_auto_renew(request: ImageRemixAutoRenewReq
|
||||
let response = generate_bearer_with_cookie(cookies).await;
|
||||
match response {
|
||||
Err(err) => {
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("failed to generate new JWT bearer token: {:?}", err)))
|
||||
return Err(SoraGenericApiError::UncategorizedBadResponse(format!("failed to generate new JWT bearer token: {:?}", err)).into())
|
||||
}
|
||||
Ok(bearer) => {
|
||||
match SoraJwtBearerToken::new(bearer) {
|
||||
Err(err) => {
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("Failed to parse new JWT bearer token: {:?}", err)));
|
||||
return Err(SoraGenericApiError::UncategorizedBadResponse(format!("Failed to parse new JWT bearer token: {:?}", err)).into());
|
||||
}
|
||||
Ok(bearer) => new_bearer = Some(bearer),
|
||||
}
|
||||
@@ -105,7 +107,7 @@ pub async fn image_remix_with_session_auto_renew(request: ImageRemixAutoRenewReq
|
||||
let response = generate_token().await;
|
||||
match response {
|
||||
Err(err) => {
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("failed to generate new sentinel: {:?}", err)))
|
||||
return Err(SoraGenericApiError::UncategorizedBadResponse(format!("failed to generate new sentinel: {:?}", err)).into())
|
||||
}
|
||||
Ok(sentinel) => new_sentinel = Some(SoraSentinel::new(sentinel)),
|
||||
}
|
||||
@@ -135,12 +137,12 @@ pub async fn image_remix_with_session_auto_renew(request: ImageRemixAutoRenewReq
|
||||
match result {
|
||||
Ok(response) => Ok((response, Some(new_creds))),
|
||||
Err(err) => match err {
|
||||
image_gen_http_request::SoraImageGenError::TooManyConcurrentTasks(err) => {
|
||||
Err(SoraError::TooManyConcurrentTasks)
|
||||
SoraImageGenError::TooManyConcurrentTasks(err) => {
|
||||
Err(SoraSpecificApiError::TooManyConcurrentTasks.into())
|
||||
}
|
||||
_ => {
|
||||
warn!("Image remix failed again: {:?}", err);
|
||||
Err(SoraError::OtherBadStatus(anyhow!("image remix failed again: {:?}", err)))
|
||||
Err(SoraGenericApiError::UncategorizedBadResponse(format!("image remix failed again: {:?}", err)).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,11 +9,11 @@ use crate::requests::image_gen::sora_image_gen_remix::{sora_image_gen_remix, Sor
|
||||
use crate::requests::sentinel_refresh::generate::token::generate_token;
|
||||
use crate::requests::upload::upload_media_from_bytes::sora_media_upload_from_bytes;
|
||||
use crate::requests::upload::upload_media_http_request::{upload_media_http_request, SoraMediaUploadResponse};
|
||||
use crate::sora_error::SoraError;
|
||||
use anyhow::anyhow;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, info, warn};
|
||||
use std::time::Duration;
|
||||
use crate::error::sora_error::SoraError;
|
||||
|
||||
pub struct ImageUploadFromBytesAutoRenewRequest<'a> {
|
||||
pub file_bytes: Vec<u8>,
|
||||
|
||||
+3
-2
@@ -1,11 +1,12 @@
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::recipes::maybe_refresh_credentials_on_sora_error::maybe_refresh_credentials_on_sora_error;
|
||||
use crate::requests::upload::upload_media_from_file::sora_media_upload_from_file;
|
||||
use crate::requests::upload::upload_media_http_request::{upload_media_http_request, SoraMediaUploadResponse};
|
||||
use crate::sora_error::SoraError;
|
||||
use log::{info, warn};
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
use crate::error::sora_specific_api_error::SoraSpecificApiError;
|
||||
|
||||
pub struct ImageUploadFromFileAutoRenewRequest<'a, P: AsRef<Path>> {
|
||||
pub file_path: P,
|
||||
@@ -31,7 +32,7 @@ pub async fn image_upload_from_file_with_session_auto_renew<P: AsRef<Path>>(
|
||||
let err = match result {
|
||||
Ok(response) => return Ok((response, None)),
|
||||
// We can't retry some errors.
|
||||
Err(err @ SoraError::SoraUsernameNotYetCreated) => return Err(err),
|
||||
Err(err @ SoraError::ApiSpecific(SoraSpecificApiError::SoraUsernameNotYetCreated)) => return Err(err),
|
||||
// Retry all other errors.
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
+1
-1
@@ -1,11 +1,11 @@
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::recipes::image_upload_from_file_with_session_auto_renew::ImageUploadFromFileAutoRenewRequest;
|
||||
use crate::recipes::maybe_refresh_credentials_on_sora_error::maybe_refresh_credentials_on_sora_error;
|
||||
use crate::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
use crate::requests::image_gen::image_gen_status::{get_image_gen_status, StatusRequest, TaskId, TaskResponse, VideoGenStatusResponse};
|
||||
use crate::requests::upload::upload_media_from_file::sora_media_upload_from_file;
|
||||
use crate::requests::upload::upload_media_http_request::SoraMediaUploadResponse;
|
||||
use crate::sora_error::SoraError;
|
||||
use anyhow::Error;
|
||||
use log::{info, warn};
|
||||
use std::path::Path;
|
||||
|
||||
+13
-9
@@ -1,7 +1,9 @@
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::creds::sora_jwt_bearer_token::SoraJwtBearerToken;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::error::sora_specific_api_error::SoraSpecificApiError;
|
||||
use crate::requests::bearer::generate_bearer_with_cookie::generate_bearer_with_cookie;
|
||||
use crate::sora_error::SoraError;
|
||||
use anyhow::anyhow;
|
||||
use log::{error, info, warn};
|
||||
|
||||
@@ -10,15 +12,15 @@ use log::{error, info, warn};
|
||||
pub async fn maybe_refresh_credentials_on_sora_error(creds: &SoraCredentialSet, error: SoraError) -> Result<SoraCredentialSet, SoraError> {
|
||||
|
||||
match error {
|
||||
SoraError::NoBearerTokenAvailable => {
|
||||
warn!("Previous request failed due to missing bearer token.");
|
||||
SoraError::Client(SoraClientError::NoBearerTokenAvailable) => {
|
||||
error!("Previous request failed due to missing bearer token.");
|
||||
}
|
||||
SoraError::UnauthorizedCookieOrBearerExpired => {
|
||||
warn!("Previous request failed due to invalid bearer token.");
|
||||
SoraError::ApiSpecific(SoraSpecificApiError::UnauthorizedCookieOrBearerExpired) => {
|
||||
error!("Previous request failed due to invalid bearer token.");
|
||||
}
|
||||
_ => {
|
||||
error!("Previous request failed with error: {:?}", error);
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("previous request failed with error: {:?}", error)))
|
||||
error!("Previous request failed due to the following error: {:?}", error);
|
||||
return Err(error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +31,14 @@ pub async fn maybe_refresh_credentials_on_sora_error(creds: &SoraCredentialSet,
|
||||
|
||||
let new_bearer = match response {
|
||||
Err(err) => {
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("failed to generate new JWT bearer token: {:?}", err)))
|
||||
error!("Error generating new JWT bearer token: {}", err);
|
||||
return Err(err);
|
||||
}
|
||||
Ok(bearer) => {
|
||||
match SoraJwtBearerToken::new(bearer) {
|
||||
Err(err) => {
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("failed to parse new JWT bearer token: {:?}", err)));
|
||||
error!("Error parsing new JWT bearer token: {}", err);
|
||||
return Err(err);
|
||||
}
|
||||
Ok(bearer) => bearer,
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use chrono::{DateTime, TimeDelta, Utc};
|
||||
use errors::AnyhowResult;
|
||||
use log::info;
|
||||
use std::ops::Sub;
|
||||
use crate::sora_error::SoraError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
|
||||
const EXPIRATION_DEADLINE : TimeDelta = TimeDelta::hours(12);
|
||||
|
||||
|
||||
+18
-13
@@ -1,16 +1,18 @@
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::creds::sora_jwt_bearer_token::SoraJwtBearerToken;
|
||||
use crate::creds::sora_sentinel::SoraSentinel;
|
||||
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::bearer::generate_bearer_with_cookie::generate_bearer_with_cookie;
|
||||
use crate::requests::image_gen::common::{ImageSize, NumImages, SoraImageGenResponse};
|
||||
use crate::requests::image_gen::sora_image_gen_remix::{sora_image_gen_remix, SoraImageGenRemixRequest};
|
||||
use crate::requests::image_gen::sora_image_gen_simple::{sora_image_gen_simple, SoraImageGenSimpleRequest};
|
||||
use crate::requests::image_gen::{image_gen_http_request, SoraImageGenError};
|
||||
use crate::requests::sentinel_refresh::generate::token::generate_token;
|
||||
use crate::sora_error::SoraError;
|
||||
use anyhow::anyhow;
|
||||
use errors::AnyhowResult;
|
||||
use log::{info, warn};
|
||||
use log::{error, info, warn};
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct SimpleImageGenAutoRenewRequest<'a> {
|
||||
@@ -46,17 +48,17 @@ pub async fn simple_image_gen_with_session_auto_renew(request: SimpleImageGenAut
|
||||
match err {
|
||||
// We'll fail these requests...
|
||||
SoraImageGenError::TooManyConcurrentTasks(_) => {
|
||||
return Err(SoraError::TooManyConcurrentTasks);
|
||||
return Err(SoraSpecificApiError::TooManyConcurrentTasks.into());
|
||||
}
|
||||
SoraImageGenError::UsernameRequired(_err) => {
|
||||
return Err(SoraSpecificApiError::SoraUsernameNotYetCreated.into())
|
||||
}
|
||||
SoraImageGenError::GenericError(err) => {
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("image gen failed with GenericError: {:?}", err)))
|
||||
return Err(SoraGenericApiError::UncategorizedBadResponse(format!("image gen failed with GenericError: {:?}", err)).into())
|
||||
}
|
||||
SoraImageGenError::NetworkError(err) => {
|
||||
// TODO: The underlying type should be a reqwest::Error.
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("network error: {:?}", err)))
|
||||
}
|
||||
SoraImageGenError::UsernameRequired(_err) => {
|
||||
return Err(SoraError::SoraUsernameNotYetCreated)
|
||||
return Err(SoraGenericApiError::UncategorizedBadResponse(format!("network error (todo: this should be a reqwest error): {:?}", err)).into())
|
||||
}
|
||||
|
||||
// We'll retry these requests...
|
||||
@@ -84,12 +86,14 @@ pub async fn simple_image_gen_with_session_auto_renew(request: SimpleImageGenAut
|
||||
let response = generate_bearer_with_cookie(cookies).await;
|
||||
match response {
|
||||
Err(err) => {
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("failed to generate new JWT bearer token: {:?}", err)))
|
||||
error!("Error asking to generate JWT: {:?}", err);
|
||||
return Err(err);
|
||||
}
|
||||
Ok(bearer) => {
|
||||
match SoraJwtBearerToken::new(bearer) {
|
||||
Err(err) => {
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("Failed to parse new JWT bearer token: {:?}", err)));
|
||||
error!("Failed to parse new JWT: {:?}", err);
|
||||
return Err(err);
|
||||
}
|
||||
Ok(bearer) => new_bearer = Some(bearer),
|
||||
}
|
||||
@@ -104,7 +108,8 @@ pub async fn simple_image_gen_with_session_auto_renew(request: SimpleImageGenAut
|
||||
let response = generate_token().await;
|
||||
match response {
|
||||
Err(err) => {
|
||||
return Err(SoraError::OtherBadStatus(anyhow!("failed to generate new sentinel: {:?}", err)))
|
||||
error!("failed to generate new sentinel: {:?}", err);
|
||||
return Err(err)
|
||||
}
|
||||
Ok(sentinel) => new_sentinel = Some(SoraSentinel::new(sentinel)),
|
||||
}
|
||||
@@ -134,11 +139,11 @@ pub async fn simple_image_gen_with_session_auto_renew(request: SimpleImageGenAut
|
||||
Ok(response) => Ok((response, Some(new_creds))),
|
||||
Err(err) => match err {
|
||||
image_gen_http_request::SoraImageGenError::TooManyConcurrentTasks(err) => {
|
||||
Err(SoraError::TooManyConcurrentTasks)
|
||||
Err(SoraSpecificApiError::TooManyConcurrentTasks.into())
|
||||
}
|
||||
_ => {
|
||||
warn!("Image gen failed again: {:?}", err);
|
||||
Err(SoraError::OtherBadStatus(anyhow!("image gen failed again: {:?}", err)))
|
||||
Err(SoraGenericApiError::UncategorizedBadResponse(format!("image gen failed again: {:?}", err)).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-6
@@ -1,5 +1,8 @@
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
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::image_gen::SoraImageGenError;
|
||||
use crate::sora_error::SoraError;
|
||||
use crate::utils::classify_general_http_error::classify_general_http_error;
|
||||
use crate::utils::classify_general_http_status_code_and_body::classify_general_http_status_code_and_body;
|
||||
use errors::AnyhowResult;
|
||||
@@ -34,7 +37,11 @@ pub struct SoraUser {
|
||||
pub async fn generate_bearer_with_cookie(cookie: &str) -> Result<String, SoraError> {
|
||||
let client = Client::builder()
|
||||
.gzip(true)
|
||||
.build()?;
|
||||
.build()
|
||||
.map_err(|err| {
|
||||
error!("Error building the client: {:?}", err);
|
||||
SoraClientError::WreqClientError(err)
|
||||
})?;
|
||||
|
||||
info!("Generating new JWT bearer token from session cookies... (cookie payload length: {})", cookie.len());
|
||||
|
||||
@@ -45,14 +52,18 @@ pub async fn generate_bearer_with_cookie(cookie: &str) -> Result<String, SoraErr
|
||||
.header("Accept-Language", "en-US,en;q=0.5")
|
||||
.header("Cookie", cookie)
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error sending request: {:?}", err);
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
let status = response.status();
|
||||
|
||||
let response_body = &response.text().await
|
||||
.map_err(|err| {
|
||||
error!("Error reading body while attempting to generate bearer token: {:?}", err);
|
||||
SoraError::WreqError(err)
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
if !status.is_success() {
|
||||
@@ -66,10 +77,14 @@ pub async fn generate_bearer_with_cookie(cookie: &str) -> Result<String, SoraErr
|
||||
|
||||
if response_body == "null" {
|
||||
error!("Failed to generate bearer token. Response was the string `null`.");
|
||||
return Err(SoraError::FailedToGenerateBearer)
|
||||
return Err(SoraSpecificApiError::FailedToGenerateBearerToken.into())
|
||||
}
|
||||
|
||||
let auth_response: SoraAuthResponse = serde_json::from_str(&response_body)?;
|
||||
let auth_response: SoraAuthResponse = serde_json::from_str(&response_body)
|
||||
.map_err(|err| {
|
||||
error!("Error parsing body while attempting to generate bearer token: {:?}", err);
|
||||
SoraGenericApiError::SerdeResponseParseErrorWithBody(err, response_body.to_string())
|
||||
})?;
|
||||
|
||||
Ok(auth_response.access_token)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::creds::sora_jwt_bearer_token::SoraJwtBearerToken;
|
||||
use crate::sora_error::SoraError;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::error::sora_generic_api_error::SoraGenericApiError;
|
||||
use crate::utils::classify_general_http_status_code_and_body::classify_general_http_status_code_and_body;
|
||||
use anyhow::anyhow;
|
||||
use errors::AnyhowResult;
|
||||
@@ -207,15 +209,17 @@ pub async fn get_image_gen_status(status_request: &StatusRequest, credentials: &
|
||||
let bearer_header = match credentials.jwt_bearer_token.as_ref() {
|
||||
Some(bearer) => bearer.to_authorization_header_value(),
|
||||
None => {
|
||||
warn!("No JWT bearer for getting image gen status!");
|
||||
return Err(SoraError::NoBearerTokenAvailable)
|
||||
warn!("No JWT bearer token in client - cannot fetch image gen status!");
|
||||
return Err(SoraClientError::NoBearerTokenAvailable.into());
|
||||
},
|
||||
};
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
let mut url = Url::parse(SORA_STATUS_URL)
|
||||
.map_err(|err| anyhow!("error parsing URL: {:?}", err))?;
|
||||
.map_err(|err| {
|
||||
SoraClientError::UrlParseError(err)
|
||||
})?;
|
||||
|
||||
// Add query parameters
|
||||
if let Some(limit) = status_request.limit {
|
||||
@@ -232,13 +236,26 @@ pub async fn get_image_gen_status(status_request: &StatusRequest, credentials: &
|
||||
.header("Authorization", bearer_header)
|
||||
.header("Content-Type", "application/json");
|
||||
|
||||
let http_request = http_request.build()?;
|
||||
let http_request = http_request.build()
|
||||
.map_err(|err| {
|
||||
SoraClientError::WreqClientError(err)
|
||||
})?;
|
||||
|
||||
let response = client.execute(http_request).await?;
|
||||
let response = client.execute(http_request)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Client failed to fetch sora image gen status: {:?}", err);
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
let status = response.status();
|
||||
|
||||
let response_body = &response.text().await?;
|
||||
let response_body = &response.text()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Client failed to read sora image gen status: {:?}", err);
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
debug!("response_body: {}", response_body);
|
||||
|
||||
@@ -254,7 +271,8 @@ pub async fn get_image_gen_status(status_request: &StatusRequest, credentials: &
|
||||
Ok(response) => Ok(response),
|
||||
Err(err) => {
|
||||
warn!("Failed to parse status response: {:?}", err);
|
||||
Err(SoraError::JsonErrorWithBody(err, response_body.to_string()))
|
||||
Err(SoraGenericApiError::SerdeResponseParseErrorWithBody(
|
||||
err, response_body.to_string()).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -316,9 +334,9 @@ mod tests {
|
||||
let bearer = bearer.trim().to_string();
|
||||
|
||||
let creds = SoraCredentialBuilder::new()
|
||||
.cookies(&cookie)
|
||||
.jwt_bearer_token(&bearer)
|
||||
.sora_sentinel(&sentinel)
|
||||
.with_cookies(&cookie)
|
||||
.with_jwt_bearer_token(&bearer)
|
||||
.with_sora_sentinel(&sentinel)
|
||||
.build()?;
|
||||
|
||||
// Get the task status for a specific task
|
||||
|
||||
+3
-3
@@ -70,9 +70,9 @@ mod tests {
|
||||
let bearer = bearer.trim().to_string();
|
||||
|
||||
let creds = SoraCredentialBuilder::new()
|
||||
.cookies(&cookie)
|
||||
.jwt_bearer_token(&bearer)
|
||||
.sora_sentinel(&sentinel)
|
||||
.with_cookies(&cookie)
|
||||
.with_jwt_bearer_token(&bearer)
|
||||
.with_sora_sentinel(&sentinel)
|
||||
.build()?;
|
||||
|
||||
let response = sora_image_gen_remix(SoraImageGenRemixRequest {
|
||||
|
||||
+3
-3
@@ -54,9 +54,9 @@ mod tests {
|
||||
let bearer = bearer.trim().to_string();
|
||||
|
||||
let creds = SoraCredentialBuilder::new()
|
||||
.cookies(&cookie)
|
||||
.jwt_bearer_token(&bearer)
|
||||
.sora_sentinel(&sentinel)
|
||||
.with_cookies(&cookie)
|
||||
.with_jwt_bearer_token(&bearer)
|
||||
.with_sora_sentinel(&sentinel)
|
||||
.build()?;
|
||||
|
||||
let response = sora_image_gen_simple(SoraImageGenSimpleRequest {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::sora_error::SoraError;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::error::sora_generic_api_error::SoraGenericApiError;
|
||||
use log::error;
|
||||
use wreq::Client;
|
||||
|
||||
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";
|
||||
@@ -12,12 +15,10 @@ pub async fn sora_job_status(
|
||||
) -> Result<(), SoraError> {
|
||||
let url = format!("https://sora.com/backend/notif?limit=100&before={}", task_id);
|
||||
|
||||
|
||||
let cookie = credentials.cookies.to_string();
|
||||
|
||||
// TODO(bt,2025-04-23): We're using a Sora payload error in place of application state error. Surface this differently.
|
||||
let authorization_header = credentials.jwt_bearer_token.as_ref()
|
||||
.ok_or(SoraError::NoBearerTokenAvailable)?
|
||||
.ok_or(SoraClientError::NoBearerTokenAvailable)?
|
||||
.to_authorization_header_value();
|
||||
|
||||
let client = Client::new();
|
||||
@@ -28,9 +29,18 @@ pub async fn sora_job_status(
|
||||
.header("User-Agent", USER_AGENT)
|
||||
.header("Cookie", &cookie);
|
||||
|
||||
let response = request.send().await?;
|
||||
let response = request.send()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("sora fetch job status error: {}", err);
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
let json_response = &response.text().await?;
|
||||
let json_response = &response.text().await
|
||||
.map_err(|err| {
|
||||
error!("sora read job status error: {}", err);
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
//let response = serde_json::from_str(json_response)?;
|
||||
|
||||
@@ -58,9 +68,9 @@ mod tests {
|
||||
let bearer = bearer.trim().to_string();
|
||||
|
||||
let creds = SoraCredentialBuilder::new()
|
||||
.cookies(&cookie)
|
||||
.jwt_bearer_token(&bearer)
|
||||
.sora_sentinel(&sentinel)
|
||||
.with_cookies(&cookie)
|
||||
.with_jwt_bearer_token(&bearer)
|
||||
.with_sora_sentinel(&sentinel)
|
||||
.build()?;
|
||||
|
||||
let task_id = "task_01jqwwrkvgeqp8jsf5mqk1jceh";
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
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::image_gen::image_gen_status::VideoGenStatusResponse;
|
||||
use crate::requests::upload::upload_media_http_request::SoraMediaUploadResponse;
|
||||
use crate::sora_error::SoraError;
|
||||
use crate::utils::classify_general_http_error::classify_general_http_error;
|
||||
use log::{error, info};
|
||||
use once_cell::sync::Lazy;
|
||||
@@ -39,13 +42,17 @@ pub async fn list_media(credentials: &SoraCredentialSet) -> Result<ListMediaResp
|
||||
let auth_header = credentials.jwt_bearer_token
|
||||
.as_ref()
|
||||
.map(|bearer| bearer.to_authorization_header_value())
|
||||
.ok_or_else(|| SoraError::NoBearerTokenAvailable)?;
|
||||
.ok_or_else(|| SoraClientError::NoBearerTokenAvailable)?;
|
||||
|
||||
let cookie = credentials.cookies.to_string();
|
||||
|
||||
let client = Client::builder()
|
||||
.gzip(true)
|
||||
.build()?;
|
||||
.build()
|
||||
.map_err(|err| {
|
||||
error!("Failed to build HTTP client: {}", err);
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
let response = client.get(SORA_MEDIA_LIST_URL)
|
||||
.header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:137.0) Gecko/20100101 Firefox/137.0")
|
||||
@@ -55,7 +62,11 @@ pub async fn list_media(credentials: &SoraCredentialSet) -> Result<ListMediaResp
|
||||
.header("Cookie", &cookie)
|
||||
.header("Authorization", &auth_header)
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Failed to fetch media list: {}", err);
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
error!("Failed to fetch media list: {}", response.status());
|
||||
@@ -65,9 +76,19 @@ pub async fn list_media(credentials: &SoraCredentialSet) -> Result<ListMediaResp
|
||||
|
||||
info!("Successfully generated bearer token.");
|
||||
|
||||
// Parse response
|
||||
let typed_response = response.json::<ListMediaResponse>().await?;
|
||||
Ok(typed_response)
|
||||
let text_body = &response.text().await
|
||||
.map_err(|err| {
|
||||
error!("sora error reading media list text body: {}", err);
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
let response = serde_json::from_str::<ListMediaResponse>(text_body)
|
||||
.map_err(|err| {
|
||||
error!("Failed to parse media list response: {}", err);
|
||||
SoraGenericApiError::SerdeResponseParseErrorWithBody(err, text_body.to_string())
|
||||
})?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
|
||||
|
||||
+25
-8
@@ -1,5 +1,7 @@
|
||||
use super::request::GenerateSentinelRefreshRequest;
|
||||
use crate::sora_error::SoraError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::error::sora_generic_api_error::SoraGenericApiError;
|
||||
use crate::requests::list_media::list_media::ListMediaResponse;
|
||||
use crate::utils::classify_general_http_error::classify_general_http_error;
|
||||
use errors::AnyhowResult;
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
@@ -49,11 +51,15 @@ pub async fn generate_token() -> Result<String, SoraError> {
|
||||
let request = SentinelRequest::new(base64_request);
|
||||
let client = Client::new();
|
||||
let response = client.post(SORA_IMAGE_GEN_URL)
|
||||
.header("User-Agent", USER_AGENT)
|
||||
.header("Content-Type", "application/json")
|
||||
.json(&request)
|
||||
.send()
|
||||
.await?;
|
||||
.header("User-Agent", USER_AGENT)
|
||||
.header("Content-Type", "application/json")
|
||||
.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());
|
||||
@@ -61,8 +67,19 @@ pub async fn generate_token() -> Result<String, SoraError> {
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
let response_json: SentinelResponse = response.json().await?;
|
||||
Ok(response_json.token)
|
||||
let text_body = &response.text().await
|
||||
.map_err(|err| {
|
||||
error!("sora error reading sentinel token text body: {}", err);
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
let response = serde_json::from_str::<SentinelResponse>(&text_body)
|
||||
.map_err(|err| {
|
||||
error!("Failed to parse media list response: {}", err);
|
||||
SoraGenericApiError::SerdeResponseParseErrorWithBody(err, text_body.to_string())
|
||||
})?;
|
||||
|
||||
Ok(response.token)
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::requests::upload::upload_media_http_request::{upload_media_http_request, SoraMediaUploadResponse};
|
||||
use crate::sora_error::SoraError;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
+21
-7
@@ -1,6 +1,8 @@
|
||||
use crate::creds::sora_credential_set::SoraCredentialSet;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::requests::upload::upload_media_http_request::{upload_media_http_request, SoraMediaUploadResponse};
|
||||
use crate::sora_error::SoraError;
|
||||
use log::error;
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
use tokio::fs::File;
|
||||
@@ -15,17 +17,29 @@ pub async fn sora_media_upload_from_file<P: AsRef<Path>>(
|
||||
creds: &SoraCredentialSet,
|
||||
maybe_timeout: Option<Duration>,
|
||||
) -> Result<SoraMediaUploadResponse, SoraError> {
|
||||
let mut file = File::open(&file_path).await?;
|
||||
let mut file = File::open(&file_path).await
|
||||
.map_err(|err| {
|
||||
error!("Failed to open file for upload: {}", err);
|
||||
SoraClientError::FileForUploadReadError(err)
|
||||
})?;
|
||||
|
||||
let mut buffer = Vec::with_capacity(INITIAL_BUFFER_SIZE);
|
||||
file.read_to_end(&mut buffer).await?;
|
||||
|
||||
file.read_to_end(&mut buffer).await
|
||||
.map_err(|err| {
|
||||
error!("Failed to read file for upload: {}", err);
|
||||
SoraClientError::FileForUploadReadError(err)
|
||||
})?;
|
||||
|
||||
let filename = file_path.as_ref().file_name()
|
||||
.ok_or_else(|| anyhow::anyhow!("Invalid file path"))?
|
||||
.ok_or_else(|| SoraClientError::FileForUploadHasInvalidPath)?
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
let maybe_ext = file_path.as_ref().extension().and_then(|e| e.to_str());
|
||||
|
||||
// TODO: Read file magic bytes first, then fall back to this.
|
||||
let mime_type = match file_path.as_ref().extension().and_then(|e| e.to_str()) {
|
||||
let mime_type = match maybe_ext {
|
||||
Some("jpg") | Some("jpeg") => "image/jpeg",
|
||||
Some("png") => "image/png",
|
||||
// Some("webp") => "image/webp",
|
||||
@@ -59,8 +73,8 @@ mod tests {
|
||||
let image_path = test_file_path("test_data/image/juno.jpg")?; // media_01jqyqgqpwf40tkcapq5bmaz5d
|
||||
|
||||
let creds = SoraCredentialBuilder::new()
|
||||
.cookies(&cookie)
|
||||
.jwt_bearer_token(&bearer)
|
||||
.with_cookies(&cookie)
|
||||
.with_jwt_bearer_token(&bearer)
|
||||
.build()?;
|
||||
|
||||
let response = sora_media_upload_from_file(
|
||||
|
||||
+28
-9
@@ -1,9 +1,13 @@
|
||||
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::bearer::generate_bearer_with_cookie::SoraAuthResponse;
|
||||
use crate::requests::image_gen::image_gen_status::USER_AGENT;
|
||||
use crate::sora_error::SoraError;
|
||||
use crate::utils::classify_general_http_error::classify_general_http_error;
|
||||
use log::info;
|
||||
use log::{error, info};
|
||||
use serde::Deserialize;
|
||||
use std::io::empty;
|
||||
use std::time::Duration;
|
||||
use wreq::multipart::{Form, Part};
|
||||
use wreq::Client;
|
||||
@@ -74,16 +78,17 @@ pub (crate) async fn upload_media_http_request(
|
||||
) -> Result<SoraMediaUploadResponse, SoraError> {
|
||||
|
||||
// Create multipart form
|
||||
let part = Part::bytes(file_bytes) // NB: Reqwest needs to own the bytes.
|
||||
.file_name(filename) // NB: Reqwest needs to own the bytes
|
||||
.mime_str(mime_type)?;
|
||||
let part = Part::bytes(file_bytes) // NB: Reqwest needs to own the bytes.
|
||||
.file_name(filename) // NB: Reqwest needs to own the bytes
|
||||
.mime_str(mime_type)
|
||||
.map_err(|e| SoraClientError::MultipartFormError(e))?;
|
||||
|
||||
let form = Form::new().part("file", part);
|
||||
|
||||
let cookie = credentials.cookies.to_string();
|
||||
let auth_header = credentials.jwt_bearer_token
|
||||
.as_ref()
|
||||
.ok_or(SoraError::NoBearerTokenAvailable)?
|
||||
.ok_or(SoraClientError::NoBearerTokenAvailable)?
|
||||
.to_authorization_header_value();
|
||||
|
||||
// Make API request
|
||||
@@ -98,7 +103,11 @@ pub (crate) async fn upload_media_http_request(
|
||||
request_builder = request_builder.timeout(timeout);
|
||||
}
|
||||
|
||||
let response = request_builder.send().await?;
|
||||
let response = request_builder.send()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
// Check response status
|
||||
if !response.status().is_success() {
|
||||
@@ -107,7 +116,17 @@ pub (crate) async fn upload_media_http_request(
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
// Parse response
|
||||
let upload_response = response.json::<SoraMediaUploadResponse>().await?;
|
||||
let response_body = &response.text().await
|
||||
.map_err(|err| {
|
||||
error!("Error reading response body while attempting file upload: {:?}", err);
|
||||
SoraGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
let upload_response : SoraMediaUploadResponse = serde_json::from_str(&response_body)
|
||||
.map_err(|err| {
|
||||
error!("Error parsing response body while attempting file upload: {:?}", err);
|
||||
SoraGenericApiError::SerdeResponseParseErrorWithBody(err, response_body.to_string())
|
||||
})?;
|
||||
|
||||
Ok(upload_response)
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
use cloudflare_errors::cloudflare_error::CloudflareError;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::io;
|
||||
|
||||
/// The single error type this crate should surface to all callers.
|
||||
#[derive(Debug)]
|
||||
pub enum SoraError {
|
||||
/// We haven't received a bearer token yet
|
||||
/// This is our own internal application state error, not something Sora returns.
|
||||
NoBearerTokenAvailable,
|
||||
|
||||
/// We're sending too many tasks to Sora.
|
||||
TooManyConcurrentTasks,
|
||||
|
||||
// TODO: Try to make concrete error types for these.
|
||||
/// Unauthorized, cookie and/or bearer token expired. We'll need to ask for a refreshed login.
|
||||
///
|
||||
/// Example message, e.g. from the upload endpoint:
|
||||
/// {
|
||||
/// "error": {
|
||||
/// "message": "Your authentication token has expired. Please try signing in again.",
|
||||
/// "type": "invalid_request_error",
|
||||
/// "param": null,
|
||||
/// "code": "token_expired"
|
||||
/// }
|
||||
/// }
|
||||
UnauthorizedCookieOrBearerExpired,
|
||||
|
||||
/// Failed to generate a bearer token despite the `200 OK` response.
|
||||
/// This has been generated by sending old/expired session cookies or empty cookies.
|
||||
FailedToGenerateBearer,
|
||||
|
||||
/// Sora requires users to set up an account username before they can generate images.
|
||||
/// This requires the user to set up that username.
|
||||
SoraUsernameNotYetCreated,
|
||||
|
||||
/// Something is wrong with the JWT bearer token.
|
||||
/// This error originates on our end as we try to parse the JWT.
|
||||
LocalJwtClaimsParseError(String),
|
||||
|
||||
/// Sora is having issues.
|
||||
/// Typically served from Cloudflare
|
||||
/// We preserve the message for debugging.
|
||||
BadGateway(String),
|
||||
|
||||
/// Specific Cloudflare errors.
|
||||
CloudflareError(CloudflareError),
|
||||
|
||||
/// Another error occurred.
|
||||
OtherBadStatus(anyhow::Error),
|
||||
|
||||
/// Reqwest Error, eg. connection failures.
|
||||
WreqError(wreq::Error),
|
||||
|
||||
/// std::io Error that arises from our end, eg. reading from the filesystem.
|
||||
IoError(io::Error),
|
||||
|
||||
/// anyhow::Error arises from our end
|
||||
AnyhowError(anyhow::Error),
|
||||
|
||||
/// Issue with setting credentials
|
||||
SoraCredentialBuilderError(String),
|
||||
|
||||
/// serde_json::Error, likely from JSON deserialization schema mismatch.
|
||||
JsonError(serde_json::Error),
|
||||
|
||||
/// serde_json::Error, likely from JSON deserialization schema mismatch.
|
||||
/// Includes the original body.
|
||||
JsonErrorWithBody(serde_json::Error, String),
|
||||
}
|
||||
|
||||
impl SoraError {
|
||||
pub fn is_sora_having_downtime_issues(&self) -> bool {
|
||||
match self {
|
||||
Self::BadGateway(_) => true,
|
||||
Self::CloudflareError(CloudflareError::GatewayTimeout504) => true,
|
||||
Self::CloudflareError(CloudflareError::TimeoutOccurred524) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for SoraError {}
|
||||
|
||||
impl Display for SoraError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::NoBearerTokenAvailable => {
|
||||
write!(f, "Unauthorized: no bearer token set")
|
||||
}
|
||||
Self::FailedToGenerateBearer => {
|
||||
write!(f, "Failed to generate bearer. Probably invalid session.")
|
||||
}
|
||||
Self::SoraUsernameNotYetCreated => {
|
||||
write!(f, "Sora username not created. Please set up a username.")
|
||||
}
|
||||
Self::LocalJwtClaimsParseError(message) => {
|
||||
write!(f, "Local JWT claims parse error: {}", message)
|
||||
}
|
||||
Self::TooManyConcurrentTasks => {
|
||||
write!(f, "Too many concurrent tasks. Please wait.")
|
||||
}
|
||||
Self::UnauthorizedCookieOrBearerExpired => {
|
||||
write!(f, "Unauthorized: cookie and/or bearer token expired")
|
||||
}
|
||||
Self::BadGateway(message) => {
|
||||
write!(f, "Bad Gateway; Sora is likely having issues: {:?}", message)
|
||||
}
|
||||
Self::CloudflareError(error) => {
|
||||
write!(f, "Cloudflare error: {}", error)
|
||||
}
|
||||
Self::OtherBadStatus(err) => {
|
||||
write!(f, "Other error: {}", err)
|
||||
}
|
||||
Self::WreqError(err) => {
|
||||
write!(f, "Wreq error: {}", err)
|
||||
}
|
||||
Self::IoError(err) => {
|
||||
write!(f, "IO error: {}", err)
|
||||
}
|
||||
Self::AnyhowError(err) => {
|
||||
write!(f, "Anyhow error: {}", err)
|
||||
}
|
||||
Self::SoraCredentialBuilderError(message) => {
|
||||
write!(f, "Sora credential builder error: {}", message)
|
||||
}
|
||||
Self::JsonError(err) => {
|
||||
write!(f, "serde_json error: {}", err)
|
||||
}
|
||||
Self::JsonErrorWithBody(err, message) => {
|
||||
write!(f, "serde_json error: {:?} body: {:?}", err, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<wreq::Error> for SoraError {
|
||||
fn from(err: wreq::Error) -> SoraError {
|
||||
Self::WreqError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for SoraError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
Self::IoError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<anyhow::Error> for SoraError {
|
||||
fn from(err: anyhow::Error) -> Self {
|
||||
Self::AnyhowError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for SoraError {
|
||||
fn from(err: serde_json::Error) -> Self {
|
||||
Self::JsonError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CloudflareError> for SoraError {
|
||||
fn from(err: CloudflareError) -> Self {
|
||||
Self::CloudflareError(err)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::sora_error::SoraError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::error::sora_generic_api_error::SoraGenericApiError;
|
||||
use crate::utils::classify_general_http_status_code_and_body::classify_general_http_status_code_and_body;
|
||||
|
||||
/// This assumes the request failed and returned a non-200.
|
||||
@@ -7,7 +8,7 @@ pub async fn classify_general_http_error(response: wreq::Response) -> SoraError
|
||||
let status = response.status();
|
||||
let message = match response.text().await {
|
||||
Ok(text) => text,
|
||||
Err(err) => return SoraError::WreqError(err),
|
||||
Err(err) => return SoraGenericApiError::WreqError(err).into(),
|
||||
};
|
||||
|
||||
classify_general_http_status_code_and_body(status, &message).await
|
||||
|
||||
+11
-6
@@ -1,4 +1,6 @@
|
||||
use crate::sora_error::SoraError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use crate::error::sora_generic_api_error::SoraGenericApiError;
|
||||
use crate::error::sora_specific_api_error::SoraSpecificApiError;
|
||||
use cloudflare_errors::filter_cloudflare_errors::filter_cloudflare_errors;
|
||||
use wreq::StatusCode;
|
||||
/*
|
||||
@@ -38,7 +40,7 @@ pub async fn classify_general_http_status_code_and_body(status: StatusCode, resp
|
||||
|| message.contains("token_expired");
|
||||
|
||||
if cookie_expired {
|
||||
return SoraError::UnauthorizedCookieOrBearerExpired;
|
||||
return SoraSpecificApiError::UnauthorizedCookieOrBearerExpired.into();
|
||||
}
|
||||
|
||||
let needs_onboarding =
|
||||
@@ -46,21 +48,24 @@ pub async fn classify_general_http_status_code_and_body(status: StatusCode, resp
|
||||
|| message.contains("onboarding_required");
|
||||
|
||||
if needs_onboarding {
|
||||
return SoraError::SoraUsernameNotYetCreated;
|
||||
return SoraSpecificApiError::SoraUsernameNotYetCreated.into();
|
||||
}
|
||||
|
||||
let status_code = status.as_u16();
|
||||
|
||||
if let Err(err) = filter_cloudflare_errors(status_code, &response_body) {
|
||||
return SoraError::CloudflareError(err);
|
||||
return SoraGenericApiError::CloudflareError(err).into();
|
||||
}
|
||||
|
||||
match status_code {
|
||||
502 => {
|
||||
return SoraError::BadGateway(message);
|
||||
return SoraGenericApiError::Http502ErrorBadGateway(message.to_string()).into();
|
||||
}
|
||||
_ => {}, // Fall-through
|
||||
}
|
||||
|
||||
SoraError::OtherBadStatus(anyhow::anyhow!("Upload failed with status {}: {}", status, message))
|
||||
SoraGenericApiError::UncategorizedBadResponseWithStatusAndBody {
|
||||
status_code: status,
|
||||
body: response_body.to_string(),
|
||||
}.into()
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Adapted from 'jwt' crate
|
||||
|
||||
use crate::sora_error::SoraError;
|
||||
use anyhow::anyhow;
|
||||
use crate::error::sora_client_error::SoraClientError;
|
||||
use crate::error::sora_error::SoraError;
|
||||
use base64::prelude::BASE64_URL_SAFE_NO_PAD;
|
||||
use base64::Engine;
|
||||
use chrono::{DateTime, Utc};
|
||||
@@ -24,11 +24,10 @@ pub fn lightweight_sora_jwt_parse(token: &str) -> Result<SoraJwtClaims, SoraErro
|
||||
let [_header_str, claims_str, _signature_str] = split_components(token)?;
|
||||
|
||||
let claims = decode_base64(claims_str)
|
||||
.map_err(|err| SoraError::LocalJwtClaimsParseError(
|
||||
format!("failure to parse claims base64: {:?}", err)))?;
|
||||
.map_err(|err| as_jwt_error(format!("failure to parse claims base64: {:?}", err)))?;
|
||||
|
||||
let claims : serde_json::Map<String, Value> = serde_json::from_str(&claims)
|
||||
.map_err(|err| SoraError::LocalJwtClaimsParseError(
|
||||
.map_err(|err| as_jwt_error(
|
||||
format!("failure to parse claims json: {:?}", err)))?;
|
||||
|
||||
let iat = claims.get("iat")
|
||||
@@ -110,7 +109,11 @@ fn decode_base64(raw: &str) -> AnyhowResult<String> {
|
||||
}
|
||||
|
||||
fn jwt_error(reason: &str) -> SoraError {
|
||||
SoraError::LocalJwtClaimsParseError(reason.to_string())
|
||||
SoraError::Client(SoraClientError::LocalJwtClaimsParseError(reason.to_string()))
|
||||
}
|
||||
|
||||
fn as_jwt_error(reason: String) -> SoraError {
|
||||
SoraError::Client(SoraClientError::LocalJwtClaimsParseError(reason))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -3,7 +3,7 @@ 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::sora_error::SoraError;
|
||||
use openai_sora_client::error::sora_error::SoraError;
|
||||
use storyteller_client::error::storyteller_error::StorytellerError;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
-1
@@ -32,7 +32,6 @@ use openai_sora_client::recipes::image_remix_with_session_auto_renew::{image_rem
|
||||
use openai_sora_client::recipes::image_upload_from_file_with_session_auto_renew::{image_upload_from_file_with_session_auto_renew, ImageUploadFromFileAutoRenewRequest};
|
||||
use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages};
|
||||
use openai_sora_client::sora_error::SoraError;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sqlite_tasks::queries::create_task::{create_task, CreateTaskArgs};
|
||||
use std::time::Duration;
|
||||
|
||||
-1
@@ -35,7 +35,6 @@ use openai_sora_client::recipes::image_remix_with_session_auto_renew::{image_rem
|
||||
use openai_sora_client::recipes::image_upload_from_file_with_session_auto_renew::{image_upload_from_file_with_session_auto_renew, ImageUploadFromFileAutoRenewRequest};
|
||||
use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages};
|
||||
use openai_sora_client::sora_error::SoraError;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sqlite_tasks::queries::create_task::{create_task, CreateTaskArgs};
|
||||
use std::time::Duration;
|
||||
|
||||
-1
@@ -34,7 +34,6 @@ use openai_sora_client::recipes::image_remix_with_session_auto_renew::{image_rem
|
||||
use openai_sora_client::recipes::image_upload_from_file_with_session_auto_renew::{image_upload_from_file_with_session_auto_renew, ImageUploadFromFileAutoRenewRequest};
|
||||
use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages};
|
||||
use openai_sora_client::sora_error::SoraError;
|
||||
use std::time::Duration;
|
||||
use storyteller_client::endpoints::media_files::get_media_file::get_media_file;
|
||||
use tauri::AppHandle;
|
||||
|
||||
-1
@@ -37,7 +37,6 @@ use openai_sora_client::recipes::image_remix_with_session_auto_renew::{image_rem
|
||||
use openai_sora_client::recipes::image_upload_from_file_with_session_auto_renew::{image_upload_from_file_with_session_auto_renew, ImageUploadFromFileAutoRenewRequest};
|
||||
use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages};
|
||||
use openai_sora_client::sora_error::SoraError;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sqlite_tasks::queries::create_task::{create_task, CreateTaskArgs};
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -14,15 +14,16 @@ use crate::services::sora::state::sora_credential_manager::SoraCredentialManager
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use errors::AnyhowError;
|
||||
use log::{error, info, warn};
|
||||
use openai_sora_client::error::sora_error::SoraError;
|
||||
use openai_sora_client::error::sora_specific_api_error::SoraSpecificApiError;
|
||||
use openai_sora_client::recipes::image_remix_with_session_auto_renew::{image_remix_with_session_auto_renew, ImageRemixAutoRenewRequest};
|
||||
use openai_sora_client::recipes::image_upload_from_file_with_session_auto_renew::{image_upload_from_file_with_session_auto_renew, ImageUploadFromFileAutoRenewRequest};
|
||||
use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages};
|
||||
use openai_sora_client::sora_error::SoraError;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
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 tauri::{AppHandle, Manager, State};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
@@ -135,12 +136,12 @@ pub async fn sora_image_remix_command(
|
||||
let mut error_message = "An error occurred. Please try again. If it continues, please tell our staff about the problem.";
|
||||
|
||||
match (err) {
|
||||
InnerError::SoraError(SoraError::TooManyConcurrentTasks) => {
|
||||
InnerError::SoraError(SoraError::ApiSpecific(SoraSpecificApiError::TooManyConcurrentTasks)) => {
|
||||
error_type = SoraImageRemixErrorType::TooManyConcurrentTasks;
|
||||
status = CommandErrorStatus::ServerError;
|
||||
error_message = "You have too many Sora image generation tasks running. Please wait a moment.";
|
||||
},
|
||||
InnerError::SoraError(SoraError::SoraUsernameNotYetCreated) => {
|
||||
InnerError::SoraError(SoraError::ApiSpecific(SoraSpecificApiError::SoraUsernameNotYetCreated)) => {
|
||||
error_type = SoraImageRemixErrorType::SoraUsernameNotYetCreated;
|
||||
status = CommandErrorStatus::BadRequest;
|
||||
error_message = "Your Sora username is not yet created. Please visit the Sora.com website to create it first.";
|
||||
|
||||
+3
-3
@@ -22,9 +22,9 @@ pub fn get_sora_credentials_from_redis(
|
||||
match (bearer, cookie, sentinel) {
|
||||
(Some(b), Some(c), Some(s)) => {
|
||||
let creds = SoraCredentialBuilder::new()
|
||||
.jwt_bearer_token(&b)
|
||||
.cookies(&c)
|
||||
.sora_sentinel(&s)
|
||||
.with_jwt_bearer_token(&b)
|
||||
.with_cookies(&c)
|
||||
.with_sora_sentinel(&s)
|
||||
.build()?;
|
||||
Ok(creds)
|
||||
}
|
||||
|
||||
+6
-6
@@ -27,9 +27,9 @@ pub fn get_sora_credentials_from_request(
|
||||
info!("Sora credentials were present in HTTP request headers!");
|
||||
|
||||
let mut creds = SoraCredentialBuilder::new()
|
||||
.cookies(cookie)
|
||||
.jwt_bearer_token(bearer)
|
||||
.sora_sentinel(sentinel)
|
||||
.with_cookies(cookie)
|
||||
.with_jwt_bearer_token(bearer)
|
||||
.with_sora_sentinel(sentinel)
|
||||
.build()?;
|
||||
|
||||
return Ok(creds);
|
||||
@@ -44,15 +44,15 @@ pub fn get_sora_credentials_from_request(
|
||||
let mut builder = credentials.to_builder();
|
||||
if let Some(bearer) = sora_bearer {
|
||||
info!("override bearer value");
|
||||
builder = builder.jwt_bearer_token(&bearer);
|
||||
builder = builder.with_jwt_bearer_token(&bearer);
|
||||
}
|
||||
if let Some(cookie) = sora_cookie {
|
||||
info!("override cookie value");
|
||||
builder = builder.cookies(&cookie)
|
||||
builder = builder.with_cookies(&cookie)
|
||||
}
|
||||
if let Some(sentinel) = sora_sentinel {
|
||||
info!("override sentinel value");
|
||||
builder = builder.sora_sentinel(&sentinel);
|
||||
builder = builder.with_sora_sentinel(&sentinel);
|
||||
}
|
||||
return Ok(builder.build()?);
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user