mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-27 04:00:10 -04:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f211332016 | ||
|
|
7ef20bc885 | ||
|
|
b031a68000 | ||
|
|
e4b648b55d | ||
|
|
d78ccbca25 | ||
|
|
bae6bc6863 | ||
|
|
faa43a7361 | ||
|
|
f8b3b708c0 | ||
|
|
4c40379470 | ||
|
|
0c32a89828 |
Generated
+31
@@ -5035,6 +5035,12 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hex-literal"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcaaec4551594c969335c98c903c1397853d4198408ea609190f420500f6be71"
|
||||
|
||||
[[package]]
|
||||
name = "hf-hub"
|
||||
version = "0.3.2"
|
||||
@@ -6130,6 +6136,15 @@ dependencies = [
|
||||
"signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
@@ -7849,14 +7864,20 @@ name = "openai_sora_client"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"crockford",
|
||||
"errors",
|
||||
"filesys",
|
||||
"hex-literal",
|
||||
"idempotency",
|
||||
"log",
|
||||
"rand 0.9.0",
|
||||
"reqwest 0.12.15",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha3",
|
||||
"subprocess_common",
|
||||
"testing",
|
||||
"thiserror 2.0.11",
|
||||
@@ -10605,6 +10626,16 @@ dependencies = [
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
"keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
|
||||
+3
-1
@@ -152,7 +152,9 @@ workspace-build-acceleration = { path = "crates/workspace-build-acceleration" }
|
||||
# Centrally pinned versions so that we can uniformly upgrade/downgrade versions across the workspace
|
||||
# If a crate needs to use an older/newer version, the dependency should be managed in that crate's own Cargo.toml
|
||||
anyhow = "1.0.97"
|
||||
base64 = "0.22.1"
|
||||
chrono = { version = "0.4.40", features = ["serde"] }
|
||||
hex-literal = "1.0.0"
|
||||
log = "0.4.27"
|
||||
once_cell = "1.21.3"
|
||||
rand = "0.9.0"
|
||||
@@ -162,10 +164,10 @@ serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_derive = "1.0.219"
|
||||
serde_json = "1.0.140"
|
||||
serde_yaml = "0.9.1"
|
||||
sha3 = "0.10.8"
|
||||
thiserror = "2"
|
||||
toml = "0.7.4"
|
||||
url = "2.5.4"
|
||||
|
||||
# Internal libs
|
||||
crockford = { path = "crates/lib/data/crockford" }
|
||||
errors = { path = "crates/lib/errors" }
|
||||
|
||||
@@ -10,7 +10,7 @@ pub fn encode_rgb_image_base64_png(
|
||||
|
||||
image.write_to(&mut Cursor::new(&mut bytes), ImageFormat::Png)?;
|
||||
|
||||
let bytes = BASE64_STANDARD.encode(bytes);
|
||||
let bytes = (&BASE64_STANDARD).encode(bytes);
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ workspace-build-acceleration = { workspace = true }
|
||||
crockford = { path = "../../data/crockford" }
|
||||
errors = { path = "../../errors" }
|
||||
subprocess_common = { path = "../../subprocess_common" }
|
||||
idempotency = { path = "../../idempotency" }
|
||||
|
||||
# External
|
||||
anyhow = { workspace = true }
|
||||
@@ -28,9 +29,14 @@ reqwest = { workspace = true, features = ["multipart"] }
|
||||
serde = { workspace = true }
|
||||
serde_derive = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
sha3 = { workspace = true }
|
||||
hex-literal = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
tokio = { version = "1.38.0", features = ["macros"] }
|
||||
toml = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
chrono = { workspace = true}
|
||||
rand = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
testing = { path = "../../testing" }
|
||||
|
||||
@@ -15,6 +15,9 @@ pub enum SoraError {
|
||||
#[error("Sora sentinel block: {0}")]
|
||||
SentinelBlock(String),
|
||||
|
||||
#[error("Sora too many concurrent tasks: {0}")]
|
||||
TooManyConcurrentTasks(String),
|
||||
|
||||
#[error("Sora API error: {0}")]
|
||||
GenericError(String),
|
||||
|
||||
@@ -115,10 +118,10 @@ pub enum RawSoraError {
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SoraErrorCode {
|
||||
pub enum SoraErrorCode {
|
||||
SentinelBlock,
|
||||
TokenExpired,
|
||||
|
||||
TooManyConcurrentTasks,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
@@ -179,6 +182,9 @@ pub (crate) async fn image_gen_http_request(sora_request: RawSoraImageGenRequest
|
||||
SoraErrorCode::SentinelBlock => {
|
||||
return Err(SoraError::SentinelBlock(error_response.error.message));
|
||||
}
|
||||
SoraErrorCode::TooManyConcurrentTasks => {
|
||||
return Err(SoraError::TooManyConcurrentTasks(error_response.error.message));
|
||||
}
|
||||
SoraErrorCode::Unknown(_) => {
|
||||
return Err(SoraError::GenericError(error_response.error.message));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,675 @@
|
||||
// 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"
|
||||
// ]
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
const CORES: &[usize] = &[8, 16, 24, 32];
|
||||
|
||||
const CACHED_SCRIPTS: &[&str] = &["https://cdn.oaistatic.com/_next/static/cXh69klOLzS0Gy2joLDRS/_ssgManifest.js?dpl=453ebaec0d44c2decab71692e1bfe39be35a24b3"];
|
||||
|
||||
const CACHED_DPL: &[&str] = &["prod-f501fe933b3edf57aea882da888e1a544df99840"];
|
||||
|
||||
const NAVIGATOR_KEY: &[&str] = &[
|
||||
"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",
|
||||
"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]"
|
||||
];
|
||||
|
||||
const DOCUMENT_KEY: &[&str] = &["_reactListeningo743lnnpvdg", "location"];
|
||||
|
||||
const WINDOW_KEY: &[&str] = &[
|
||||
"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"
|
||||
];
|
||||
|
||||
pub fn get_random_core_count() -> usize {
|
||||
return CORES[rand::rng().random_range(0..CORES.len())];
|
||||
}
|
||||
|
||||
|
||||
pub fn get_random_navigator_key() -> String {
|
||||
return NAVIGATOR_KEY[rand::rng().random_range(0..NAVIGATOR_KEY.len())].to_string();
|
||||
}
|
||||
|
||||
pub fn get_random_document_key() -> String {
|
||||
return DOCUMENT_KEY[rand::rng().random_range(0..DOCUMENT_KEY.len())].to_string();
|
||||
}
|
||||
|
||||
pub fn get_random_window_key() -> String {
|
||||
return WINDOW_KEY[rand::rng().random_range(0..WINDOW_KEY.len())].to_string();
|
||||
}
|
||||
|
||||
pub fn get_random_cached_script() -> String {
|
||||
return CACHED_SCRIPTS[rand::rng().random_range(0..CACHED_SCRIPTS.len())].to_string();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod request;
|
||||
pub mod token;
|
||||
pub(crate) mod config;
|
||||
@@ -0,0 +1,226 @@
|
||||
use crate::sentinel_refresh::generate::config::{get_random_core_count, get_random_document_key, get_random_navigator_key, get_random_window_key};
|
||||
use chrono::FixedOffset;
|
||||
use chrono::Utc;
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
use rand::Rng;
|
||||
use base64::prelude::BASE64_STANDARD;
|
||||
use base64::Engine;
|
||||
use sha3::{Digest, Sha3_512};
|
||||
|
||||
// [
|
||||
// 4000, // first
|
||||
// "Fri Apr 11 2025 13:28:14 GMT+0530 (India Standard Time)", // second
|
||||
// None, // third
|
||||
// 28, // fourth
|
||||
// "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:137.0) Gecko/20100101 Firefox/137.0", // fifth
|
||||
// "https://sora-cdn.oaistatic.com/_next/static/chunks/f90f2e60-61da6d33257661c4.js", // sixth
|
||||
// None, // seventh
|
||||
// "en-US", // eighth
|
||||
// "en-US,en", // ninth
|
||||
// 29, // tenth
|
||||
// "productSub−20100101", // eleventh
|
||||
// "location", // twelfth
|
||||
// "ondrop", // thirteenth
|
||||
// 10520, // fourteenth - generated from time.perf_counter() * 1000
|
||||
// "48094014-a024-4d2d-8ec4-ba9c7cfef9c4", // fifteenth
|
||||
// "", // sixteenth
|
||||
// 16, // seventeenth - Number of cores
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GenerateSentinelRefreshRequest {
|
||||
first: usize,
|
||||
second: String, // Date
|
||||
third: Option<String>,
|
||||
fourth: Option<usize>,
|
||||
fifth: String, // User agent
|
||||
sixth: String, // URL
|
||||
seventh: Option<String>, // Language
|
||||
eighth: String, // Accept-Language
|
||||
ninth: String, // Product Sub
|
||||
tenth: Option<usize>,
|
||||
eleventh: String, // Product Sub - Navigator key
|
||||
twelfth: String, // Location - Document key
|
||||
thirteenth: String, // On Drop - Window key
|
||||
fourteenth: usize, // generated from time.perf_counter() * 1000
|
||||
fifteenth: String, // UUID
|
||||
sixteenth: String, // empty string
|
||||
seventeenth: usize, // Number of cores
|
||||
}
|
||||
|
||||
fn generate_date() -> String {
|
||||
let now_utc = Utc::now();
|
||||
let today_utc = now_utc.date_naive();
|
||||
let offset = FixedOffset::west_opt(5 * 60 * 60).unwrap();
|
||||
let now_with_offset = Utc::now().with_timezone(&offset);
|
||||
|
||||
let formatted_date = now_with_offset.format("%a %b %d %Y %H:%M:%S GMT-0500 (Eastern Standard Time)");
|
||||
return formatted_date.to_string();
|
||||
}
|
||||
|
||||
impl GenerateSentinelRefreshRequest {
|
||||
pub fn new() -> Self {
|
||||
let first_candidates = vec![1920 + 1080, 2560 + 1440, 1920 + 1200, 2560 + 1600];
|
||||
let first = first_candidates[rand::rng().random_range(0..first_candidates.len())];
|
||||
|
||||
let second = generate_date();
|
||||
|
||||
let third = None;
|
||||
|
||||
let fourth = None;
|
||||
|
||||
let fifth = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:137.0) Gecko/20100101 Firefox/137.0".to_string();
|
||||
|
||||
let sixth = "https://sora-cdn.oaistatic.com/_next/static/chunks/f90f2e60-61da6d33257661c4.js".to_string();
|
||||
|
||||
let seventh = None;
|
||||
|
||||
let eighth = "en-US".to_string();
|
||||
|
||||
let ninth = "en-US,en".to_string();
|
||||
|
||||
let tenth = None;
|
||||
|
||||
let eleventh = get_random_navigator_key();
|
||||
|
||||
let twelfth = get_random_document_key();
|
||||
|
||||
let thirteenth = get_random_window_key();
|
||||
|
||||
let fourteenth = 10520;
|
||||
|
||||
let fifteenth = generate_random_uuid();
|
||||
|
||||
let sixteenth = "".to_string();
|
||||
|
||||
let seventeenth = get_random_core_count();
|
||||
Self { first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth, eleventh: eleventh.to_string(), twelfth: twelfth.to_string(), thirteenth: thirteenth.to_string(), fourteenth, fifteenth, sixteenth, seventeenth }
|
||||
}
|
||||
|
||||
pub fn with_fourth(mut self, fourth: usize) -> Self {
|
||||
self.fourth = Some(fourth);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_tenth(mut self, tenth: usize) -> Self {
|
||||
self.tenth = Some(tenth);
|
||||
self
|
||||
}
|
||||
|
||||
//TODO(kasisnu): This is a temporary function to build the request.
|
||||
// It is extremely hacky and this whole struct should be rewritten to be more ergonomic.
|
||||
pub fn build(self) -> String {
|
||||
let mut request = String::new();
|
||||
request.push_str("[");
|
||||
request.push_str(&self.first.to_string());
|
||||
request.push_str(",");
|
||||
request.push_str(&string_with_quotes(self.second));
|
||||
request.push_str(",");
|
||||
request.push_str(&self.third.unwrap_or("null".to_string()));
|
||||
request.push_str(",");
|
||||
request.push_str(&self.fourth.unwrap_or(0).to_string());
|
||||
request.push_str(",");
|
||||
request.push_str(&string_with_quotes(self.fifth));
|
||||
request.push_str(",");
|
||||
request.push_str(&string_with_quotes(self.sixth));
|
||||
request.push_str(",");
|
||||
request.push_str(&self.seventh.unwrap_or("null".to_string()));
|
||||
request.push_str(",");
|
||||
request.push_str(&string_with_quotes(self.eighth));
|
||||
request.push_str(",");
|
||||
request.push_str(&string_with_quotes(self.ninth));
|
||||
request.push_str(",");
|
||||
request.push_str(&self.tenth.unwrap_or(0).to_string());
|
||||
request.push_str(",");
|
||||
request.push_str(&string_with_quotes(self.eleventh));
|
||||
request.push_str(",");
|
||||
request.push_str(&string_with_quotes(self.twelfth));
|
||||
request.push_str(",");
|
||||
request.push_str(&string_with_quotes(self.thirteenth));
|
||||
request.push_str(",");
|
||||
request.push_str(&self.fourteenth.to_string());
|
||||
request.push_str(",");
|
||||
request.push_str(&string_with_quotes(self.fifteenth));
|
||||
request.push_str(",");
|
||||
request.push_str(&string_with_quotes(self.sixteenth));
|
||||
request.push_str(",");
|
||||
request.push_str(&self.seventeenth.to_string());
|
||||
request.push_str("]");
|
||||
request
|
||||
}
|
||||
|
||||
pub fn hash_request(seed: f64, request: String) -> Vec<u8> {
|
||||
let mut hasher = Sha3_512::new();
|
||||
hasher.update(seed.to_string());
|
||||
let base64_request = BASE64_STANDARD.encode(request.as_bytes());
|
||||
hasher.update(base64_request);
|
||||
hasher.finalize().to_vec()
|
||||
}
|
||||
|
||||
pub fn with_fourth_and_tenth(mut self) -> (Self, String) {
|
||||
let mut fourth = 0;
|
||||
let mut tenth = 0;
|
||||
let max_iterations = 500000;
|
||||
let diff_len = 6;
|
||||
let target_bytes = vec![0x0f, 0xff, 0xff];
|
||||
let random_seed: f64 = rand::rng().random();
|
||||
|
||||
let mut base64_request = BASE64_STANDARD.encode(self.clone().build().as_bytes());
|
||||
for i in 0..max_iterations {
|
||||
fourth = i;
|
||||
tenth = i >> 1;
|
||||
let request = self.clone().with_fourth(fourth).with_tenth(tenth).build();
|
||||
let hash_bytes = Self::hash_request(random_seed, request.clone());
|
||||
base64_request = BASE64_STANDARD.encode(request.as_bytes());
|
||||
|
||||
let mut is_less_or_equal = true;
|
||||
for j in 0..std::cmp::min(diff_len, target_bytes.len()) {
|
||||
if j < hash_bytes.len() {
|
||||
if hash_bytes[j] > target_bytes[j] {
|
||||
is_less_or_equal = false;
|
||||
break;
|
||||
} else if hash_bytes[j] < target_bytes[j] {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if is_less_or_equal {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(self.with_fourth(fourth).with_tenth(tenth), format!("gAAAAAC{}", base64_request))
|
||||
}
|
||||
}
|
||||
|
||||
fn string_with_quotes(value: String) -> String {
|
||||
format!("\"{}\"", value)
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_generate_sentinel_refresh_request() {
|
||||
let request = GenerateSentinelRefreshRequest::new();
|
||||
println!("{}", request.build());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_fourth_and_tenth() {
|
||||
let request = GenerateSentinelRefreshRequest::new().with_fourth(100).with_tenth(200);
|
||||
println!("{}", request.build());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_request() {
|
||||
let request = GenerateSentinelRefreshRequest::new().with_fourth(100).with_tenth(200);
|
||||
let hash = GenerateSentinelRefreshRequest::hash_request(0.1, request.build());
|
||||
println!("{}", hash.iter().map(|b| format!("{:02x}", b)).collect::<String>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_fourth_and_tenth_2() {
|
||||
let (request, base64_request) = GenerateSentinelRefreshRequest::new().with_fourth_and_tenth();
|
||||
println!("{}", base64_request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
use errors::AnyhowResult;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use idempotency::uuid::generate_random_uuid;
|
||||
|
||||
const SORA_IMAGE_GEN_URL: &str = "https://chatgpt.com/backend-api/sentinel/req";
|
||||
|
||||
/// This user agent is tied to the sentinel generation. If we need to change it, we may need to change sentinel generation too.
|
||||
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";
|
||||
|
||||
const SENTINEL_FLOW: &str = "sora_create_task";
|
||||
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SentinelRequest {
|
||||
#[serde(rename = "p")]
|
||||
problem: String,
|
||||
|
||||
#[serde(rename = "id")]
|
||||
id: String,
|
||||
|
||||
#[serde(rename = "flow")]
|
||||
flow: String,
|
||||
}
|
||||
|
||||
|
||||
impl SentinelRequest {
|
||||
pub fn new(problem: String) -> Self {
|
||||
let id = generate_random_uuid();
|
||||
Self { problem, id, flow: SENTINEL_FLOW.to_string() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SentinelResponse {
|
||||
persona: String,
|
||||
token: String,
|
||||
}
|
||||
|
||||
|
||||
pub async fn generate_token(problem: String) -> AnyhowResult<String> {
|
||||
let request = SentinelRequest::new(problem);
|
||||
let client = reqwest::Client::new();
|
||||
let response = client.post(SORA_IMAGE_GEN_URL)
|
||||
.header("User-Agent", USER_AGENT)
|
||||
.header("Content-Type", "application/json")
|
||||
.json(&request)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let response_json: SentinelResponse = response.json().await?;
|
||||
Ok(response_json.token)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::sentinel_refresh::generate::request::GenerateSentinelRefreshRequest;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_generate_token() {
|
||||
let (_request, base64_request) = GenerateSentinelRefreshRequest::new().with_fourth_and_tenth();
|
||||
let token = generate_token(base64_request.to_string()).await.unwrap();
|
||||
println!("{}", token);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,4 @@
|
||||
pub use refresh::refresh_sentinel;
|
||||
pub mod refresh;
|
||||
pub mod refresh;
|
||||
|
||||
pub mod generate;
|
||||
+62
-7
@@ -76,6 +76,9 @@ pub struct EnqueueStudioImageGenRequest {
|
||||
/// The user's image generation prompt.
|
||||
pub prompt: String,
|
||||
|
||||
/// Turn off the system prompt.
|
||||
pub disable_system_prompt: Option<bool>,
|
||||
|
||||
/// Additional images to include (optional). Up to nine images.
|
||||
pub maybe_additional_images: Option<Vec<MediaFileToken>>,
|
||||
|
||||
@@ -283,9 +286,15 @@ pub async fn enqueue_studio_image_generation_handler(http_request: HttpRequest,
|
||||
None => 1,
|
||||
};
|
||||
|
||||
let prompt = request.prompt.clone();
|
||||
let prompt = create_prompt(&request);
|
||||
|
||||
let mut response = sora_image_gen_remix(SoraImageGenRemixRequest { prompt: prompt.clone(), num_images: NumImages::One, image_size: ImageSize::Square, sora_media_tokens: sora_media_tokens.clone(), credentials: &sora_credentials }).await;
|
||||
let mut response = sora_image_gen_remix(SoraImageGenRemixRequest {
|
||||
prompt: prompt.clone(),
|
||||
num_images: NumImages::One,
|
||||
image_size: ImageSize::Square,
|
||||
sora_media_tokens: sora_media_tokens.clone(),
|
||||
credentials: &sora_credentials
|
||||
}).await;
|
||||
|
||||
debug!("Sora image gen response: {:?}", response);
|
||||
|
||||
@@ -296,13 +305,19 @@ pub async fn enqueue_studio_image_generation_handler(http_request: HttpRequest,
|
||||
match refresh_sentinel().await {
|
||||
Ok(new_sentinel) => {
|
||||
// Update the credentials with new sentinel
|
||||
|
||||
let updated_sora_credentials = get_sora_credentials_from_request(&http_request, &mut redis).map_err(|e| {
|
||||
error!("sora credential error: {:?}", e);
|
||||
EnqueueImageGenRequestError::ServerError
|
||||
})?;
|
||||
|
||||
// Retry the request with new sentinel
|
||||
response = sora_image_gen_remix(SoraImageGenRemixRequest { prompt: prompt.clone(), num_images: NumImages::One, image_size: ImageSize::Square, sora_media_tokens: sora_media_tokens.clone(), credentials: &updated_sora_credentials }).await;
|
||||
response = sora_image_gen_remix(SoraImageGenRemixRequest {
|
||||
prompt: prompt.clone(),
|
||||
num_images: NumImages::One,
|
||||
image_size: ImageSize::Square,
|
||||
sora_media_tokens: sora_media_tokens.clone(),
|
||||
credentials: &updated_sora_credentials
|
||||
}).await;
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to refresh Sora sentinel: {:?}", e);
|
||||
@@ -329,7 +344,14 @@ pub async fn enqueue_studio_image_generation_handler(http_request: HttpRequest,
|
||||
debug!("Sora image gen response: {:?}", response);
|
||||
|
||||
// Store the actual inference args that will go to the database
|
||||
let inference_args = SoraImageGenArgs { prompt: Some(prompt), scene_snapshot_media_token: Some(request.snapshot_media_token.clone()), maybe_additional_media_file_tokens: Some(additional_images), maybe_number_of_samples: Some(number_of_samples), maybe_sora_media_upload_tokens: Some(sora_media_tokens), maybe_sora_task_id: Some(response.task_id) };
|
||||
let inference_args = SoraImageGenArgs {
|
||||
prompt: Some(prompt),
|
||||
scene_snapshot_media_token: Some(request.snapshot_media_token.clone()),
|
||||
maybe_additional_media_file_tokens: Some(additional_images),
|
||||
maybe_number_of_samples: Some(number_of_samples),
|
||||
maybe_sora_media_upload_tokens: Some(sora_media_tokens),
|
||||
maybe_sora_task_id: Some(response.task_id)
|
||||
};
|
||||
|
||||
// create the inference args here
|
||||
let maybe_avt_token = server_state.avt_cookie_manager.get_avt_token_from_request(&http_request);
|
||||
@@ -348,7 +370,10 @@ pub async fn enqueue_studio_image_generation_handler(http_request: HttpRequest,
|
||||
maybe_cover_image_media_file_token: None,
|
||||
maybe_raw_inference_text: None,
|
||||
maybe_max_duration_seconds: None,
|
||||
maybe_inference_args: Some(GenericInferenceArgs { inference_category: Some(InferenceCategoryAbbreviated::ImageGeneration), args: Some(PolymorphicInferenceArgs::Sg(inference_args)) }),
|
||||
maybe_inference_args: Some(GenericInferenceArgs {
|
||||
inference_category: Some(InferenceCategoryAbbreviated::ImageGeneration),
|
||||
args: Some(PolymorphicInferenceArgs::Sg(inference_args)),
|
||||
}),
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_avt_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
@@ -412,7 +437,10 @@ async fn query_and_download_media_file(media_file_token: &MediaFileToken, downlo
|
||||
},
|
||||
};
|
||||
|
||||
let media_file_bucket_path = MediaFileBucketPath::from_object_hash(&media_file.public_bucket_directory_hash, media_file.maybe_public_bucket_prefix.as_deref(), media_file.maybe_public_bucket_extension.as_deref());
|
||||
let media_file_bucket_path = MediaFileBucketPath::from_object_hash(
|
||||
&media_file.public_bucket_directory_hash,
|
||||
media_file.maybe_public_bucket_prefix.as_deref(),
|
||||
media_file.maybe_public_bucket_extension.as_deref());
|
||||
|
||||
let extension = media_file.maybe_public_bucket_extension.clone().unwrap_or_else(|| ".jpg".to_string());
|
||||
let download_filename = format!("download_file_{}.{}", media_file.token.as_str(), extension);
|
||||
@@ -424,3 +452,30 @@ async fn query_and_download_media_file(media_file_token: &MediaFileToken, downlo
|
||||
|
||||
Ok(download_file_path)
|
||||
}
|
||||
|
||||
const SYSTEM_PROMPT_SINGLE_IMAGE: &str =
|
||||
"Use the image as a reference for the composition exactly. Copy the character pose, layout, scene, and the objects exactly - no deviation.";
|
||||
|
||||
const SYSTEM_PROMPT_MULTI_IMAGE: &str =
|
||||
"Use the first image as a reference for the composition exactly. Copy the character pose, layout, scene, and the objects exactly from the first image - no deviation.";
|
||||
|
||||
fn create_prompt(request: &EnqueueStudioImageGenRequest) -> String {
|
||||
let disable_system_prompt = request.disable_system_prompt
|
||||
.clone()
|
||||
.unwrap_or(false);
|
||||
|
||||
if disable_system_prompt {
|
||||
return request.prompt.clone();
|
||||
}
|
||||
|
||||
let extra_image_count = request.maybe_additional_images
|
||||
.as_ref()
|
||||
.map(|v| v.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
if extra_image_count == 0 {
|
||||
format!("{} {}", SYSTEM_PROMPT_SINGLE_IMAGE, request.prompt)
|
||||
} else {
|
||||
format!("{} {}", SYSTEM_PROMPT_MULTI_IMAGE, request.prompt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,10 +280,12 @@ export class Api extends ApiManager {
|
||||
}
|
||||
|
||||
public async enqueueImageGeneration({
|
||||
disableSystemPrompt,
|
||||
prompt,
|
||||
snapshotMediaToken,
|
||||
additionalImages,
|
||||
}: {
|
||||
disableSystemPrompt: boolean;
|
||||
prompt: string;
|
||||
snapshotMediaToken: string;
|
||||
additionalImages?: string[];
|
||||
@@ -293,6 +295,7 @@ export class Api extends ApiManager {
|
||||
const uuidIdempotencyToken = crypto.randomUUID(); // Generate a new UUID
|
||||
const body = {
|
||||
uuid_idempotency_token: uuidIdempotencyToken,
|
||||
disable_system_prompt: disableSystemPrompt,
|
||||
prompt,
|
||||
snapshot_media_token: snapshotMediaToken, // Changed from scene_media_token to snapshot_media_token
|
||||
additional_images: additionalImages,
|
||||
@@ -301,6 +304,7 @@ export class Api extends ApiManager {
|
||||
const postResponse = await this.post<
|
||||
{
|
||||
uuid_idempotency_token: string;
|
||||
disable_system_prompt: boolean;
|
||||
prompt: string;
|
||||
snapshot_media_token: string;
|
||||
additional_images?: string[];
|
||||
|
||||
@@ -55,7 +55,7 @@ export class Engine {
|
||||
private uiLayer: Konva.Layer;
|
||||
private previewLayer: Konva.Layer;
|
||||
private offScreenCanvas: OffscreenCanvas;
|
||||
private realTimeDrawEngine: RealTimeDrawEngine;
|
||||
public realTimeDrawEngine: RealTimeDrawEngine;
|
||||
private nodesManager: NodesManager;
|
||||
private nodeIsolator: NodeIsolator;
|
||||
private nodeTransformer: NodeTransformer;
|
||||
@@ -66,8 +66,8 @@ export class Engine {
|
||||
private backgroundNode: ShapeNode | null = null;
|
||||
|
||||
private sceneManager: SceneManager;
|
||||
private undoStackManager: UndoStackManager;
|
||||
private commandManager: CommandManager;
|
||||
public undoStackManager: UndoStackManager;
|
||||
public commandManager: CommandManager;
|
||||
private videoExtractionHandler: VideoExtractionHandler;
|
||||
|
||||
public segmentationButtonCanBePressed: boolean = true;
|
||||
|
||||
@@ -56,11 +56,8 @@ export class ShapeNode extends NetworkedNode {
|
||||
switch (shapeType) {
|
||||
case ShapeType.CIRCLE:
|
||||
shape = new Konva.Ellipse({
|
||||
x: size.width,
|
||||
y: size.height,
|
||||
radiusX: size.width,
|
||||
radiusY: size.height,
|
||||
...transform,
|
||||
radiusX: size.width / 2,
|
||||
radiusY: size.height / 2,
|
||||
fill: color,
|
||||
strokeScaleEnabled: false,
|
||||
draggable: true,
|
||||
@@ -80,8 +77,6 @@ export class ShapeNode extends NetworkedNode {
|
||||
|
||||
case ShapeType.TRIANGLE:
|
||||
shape = new Konva.Shape({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
...transform,
|
||||
@@ -113,12 +108,20 @@ export class ShapeNode extends NetworkedNode {
|
||||
this.size = size;
|
||||
this.color = color;
|
||||
|
||||
// Add shape to group
|
||||
// Center the shape
|
||||
const centerPosition = NodeUtilities.positionNodeOnCanvasCenter({
|
||||
canvasOffset: canvasPosition,
|
||||
componentSize: canvasSize,
|
||||
componentSize: this.size,
|
||||
maxSize: canvasSize,
|
||||
});
|
||||
|
||||
// If the shape is an ellipse, the center is the origin so we need to offset it
|
||||
if (shapeType === ShapeType.CIRCLE) {
|
||||
centerPosition.x += size.width / 2;
|
||||
centerPosition.y += size.height / 2;
|
||||
}
|
||||
|
||||
// Add shape to group
|
||||
// bring to the front
|
||||
this.kNode.moveToTop();
|
||||
this.kNode.setPosition(centerPosition);
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { useCallback, useRef } from "react";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
|
||||
// Components
|
||||
import { KonvaCanvasContainer } from "./KonvaCanvasContainer";
|
||||
import { ContextualButtonRetry } from "./ContextualButtonRetry";
|
||||
import { ContextualToolbarNode } from "./ContextualToolbarNode";
|
||||
import { ContextualLoadingBar } from "./ContextualLoadingBar";
|
||||
import { SignaledCanvasDragDropFiles } from "./SignaledCanvasDragDropFiles";
|
||||
import { SignaledDialogs } from "./SignaledDialogs";
|
||||
import { SignaledToolbarMain } from "./SignaledToolbarMain";
|
||||
import { SignaledToolbarVideoExtraction } from "./SignaledToolbarVideoExtraction";
|
||||
|
||||
import { PromptBox } from "~/components/PromptBox";
|
||||
// The KonvaApp is the root of the Konva stage
|
||||
// and only entry point for anything in Konva JS
|
||||
@@ -22,9 +20,8 @@ import { useLayoutContext } from "./contextSignals/layout";
|
||||
// common hooks
|
||||
import { useRenderCounter } from "~/hooks/useRenderCounter";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { SignaledMagicBox } from "./SignaledMagicBox";
|
||||
import { SignaledPromptSlider } from "./SignaledPromptSlider";
|
||||
import { SignaledPromptText } from "./SignaledPromptText";
|
||||
|
||||
import { UndoRedo } from "~/components/reusable/UndoRedo/UndoRedo";
|
||||
|
||||
export const KonvaRootComponent = ({
|
||||
className,
|
||||
@@ -39,44 +36,66 @@ export const KonvaRootComponent = ({
|
||||
const navigate = useNavigate();
|
||||
const appUiContext = useAppUiContext();
|
||||
const layoutContext = useLayoutContext();
|
||||
|
||||
// Add state to track engine initialization
|
||||
const [isEngineReady, setIsEngineReady] = useState(false);
|
||||
const engineRef = useRef<EngineType | null>(null);
|
||||
|
||||
|
||||
const konvaContainerCallbackRef = useCallback((node: HTMLDivElement) => {
|
||||
if (node !== null && engineRef.current === null) {
|
||||
const options = {
|
||||
navigate: navigate,
|
||||
sceneToken: sceneToken,
|
||||
};
|
||||
engineRef.current = KonvaApp(node, options);
|
||||
// Only initialize if we have a node and haven't initialized yet
|
||||
if (node !== null && !isEngineReady && engineRef.current === null) {
|
||||
try {
|
||||
const options = {
|
||||
navigate: navigate,
|
||||
sceneToken: sceneToken,
|
||||
};
|
||||
engineRef.current = KonvaApp(node, options);
|
||||
// Only set ready if initialization succeeded
|
||||
if (engineRef.current) {
|
||||
setIsEngineReady(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize KonvaApp:", error);
|
||||
engineRef.current = null;
|
||||
setIsEngineReady(false);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
}, [navigate, sceneToken, isEngineReady]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Always render the canvas container */}
|
||||
<KonvaCanvasContainer
|
||||
ref={konvaContainerCallbackRef}
|
||||
className={className}
|
||||
// retreive the classNames from the parent for sizing/styling
|
||||
/>
|
||||
{/* <SignaledMagicBox /> */}
|
||||
<SignaledCanvasDragDropFiles
|
||||
openAddImage={appUiContext.openAddImage}
|
||||
openAddVideo={appUiContext.openAddVideo}
|
||||
/>
|
||||
<PromptBox />
|
||||
<SignaledToolbarMain
|
||||
layoutSignal={layoutContext.signal}
|
||||
appUiContext={appUiContext}
|
||||
/>
|
||||
{/* <SignaledToolbarVideoExtraction /> */}
|
||||
<SignaledDialogs
|
||||
appUiSignal={appUiContext.signal}
|
||||
resetAll={appUiContext.resetAll}
|
||||
/>
|
||||
|
||||
{/* Conditionally render the loading overlay */}
|
||||
{!isEngineReady && (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="text-gray-500">Initializing canvas...</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* <ContextualLoadingBar /> */}
|
||||
<ContextualToolbarNode />
|
||||
{/* <ContextualButtonRetry /> */}
|
||||
{/* Conditionally render the UI components that need the engine */}
|
||||
{isEngineReady && engineRef.current && (
|
||||
<UndoRedo engine={engineRef.current}>
|
||||
<SignaledCanvasDragDropFiles
|
||||
openAddImage={appUiContext.openAddImage}
|
||||
openAddVideo={appUiContext.openAddVideo}
|
||||
/>
|
||||
<PromptBox />
|
||||
<SignaledToolbarMain
|
||||
layoutSignal={layoutContext.signal}
|
||||
appUiContext={appUiContext}
|
||||
/>
|
||||
<SignaledDialogs
|
||||
appUiSignal={appUiContext.signal}
|
||||
resetAll={appUiContext.resetAll}
|
||||
/>
|
||||
<ContextualToolbarNode />
|
||||
</UndoRedo>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -236,10 +236,10 @@ export const PromptBox = () => {
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("useSystemPrompt", useSystemPrompt);
|
||||
console.log("Snapshot media token:", snapshotMediaToken.data);
|
||||
|
||||
const response = await api.enqueueImageGeneration({
|
||||
disableSystemPrompt: !useSystemPrompt,
|
||||
prompt: prompt,
|
||||
snapshotMediaToken: snapshotMediaToken.data,
|
||||
additionalImages: referenceImages.map((image) => image.mediaToken),
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { useRef, useCallback, useContext, ReactNode, useEffect } from "react";
|
||||
import { EngineType } from "~/KonvaApp";
|
||||
interface UndoRedoProps {
|
||||
children: ReactNode;
|
||||
engine: EngineType | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A transparent component that provides undo/redo functionality
|
||||
* with keyboard shortcuts for macOS (Command+Z, Command+Shift+Z)
|
||||
*/
|
||||
export const UndoRedo = ({
|
||||
engine,
|
||||
children,
|
||||
}: UndoRedoProps) => {
|
||||
console.log("UndoRedo");
|
||||
// We don't need a separate ref since we want to track the prop changes
|
||||
// Create the event handler with useCallback
|
||||
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
if (!engine) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't capture events when user is editing text
|
||||
const target = e.target as HTMLElement;
|
||||
const isEditingText = target.tagName === 'INPUT' ||
|
||||
target.tagName === 'TEXTAREA' ||
|
||||
target.isContentEditable;
|
||||
|
||||
// Command+Z for Undo
|
||||
// Ctrl+Shift+Z for Redo (Windows/Linux alternative)
|
||||
// Windows/Linux: Ctrl+Shift+Z for Redo
|
||||
if (!navigator.userAgent.includes('Mac') && e.ctrlKey && e.shiftKey && e.key === 'z') {
|
||||
console.log("Ctrl+Shift+Z pressed: Redo (Windows/Linux)");
|
||||
e.preventDefault();
|
||||
engine.undoStackManager.redo();
|
||||
}
|
||||
|
||||
// Delete key handling - only if not editing text
|
||||
if (!isEditingText && (e.key === 'Delete' || e.key === 'Backspace')) {
|
||||
console.log("Delete/Backspace pressed: Delete selected nodes");
|
||||
e.preventDefault();
|
||||
engine.commandManager.deleteNodes();
|
||||
}
|
||||
|
||||
// macOS: Command+Z for Undo
|
||||
if (navigator.userAgent.includes('Mac') && e.metaKey && e.key === 'z' && !e.shiftKey) {
|
||||
console.log("Command+Z pressed: Undo");
|
||||
e.preventDefault();
|
||||
engine.undoStackManager.undo();
|
||||
}
|
||||
|
||||
// macOS: Command+Shift+Z for Redo
|
||||
if (navigator.userAgent.includes('Mac') && e.metaKey && e.key === 'z' && e.shiftKey) {
|
||||
console.log("Command+Shift+Z pressed: Redo");
|
||||
e.preventDefault();
|
||||
engine.undoStackManager.redo();
|
||||
engine.realTimeDrawEngine.render();
|
||||
}
|
||||
}, [engine]);
|
||||
|
||||
// Actually set up the event listeners using useEffect
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
// Cleanup function to remove the event listener
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [handleKeyDown]); // Depend on the memoized handler
|
||||
|
||||
return children;
|
||||
};
|
||||
@@ -105,8 +105,8 @@ export function Activity() {
|
||||
public_bucket_path: job.data.result.image_url || "",
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
setCompletedItems((prev) => [newCompletedItem, ...prev]);
|
||||
toast.success("Done Processing Image");
|
||||
setCompletedItems((prev) => [newCompletedItem, ...prev]);
|
||||
}
|
||||
} else {
|
||||
// Store the job in our map
|
||||
|
||||
@@ -87,19 +87,22 @@ export class EngineApi extends ApiManager {
|
||||
}
|
||||
|
||||
public async enqueueImageGeneration({
|
||||
disableSystemPrompt,
|
||||
prompt,
|
||||
snapshotMediaToken,
|
||||
additionalImages,
|
||||
}: {
|
||||
disableSystemPrompt: boolean;
|
||||
prompt: string;
|
||||
snapshotMediaToken: string;
|
||||
additionalImages?: string[];
|
||||
}): Promise<ApiResponse<string>> {
|
||||
const endpoint = `${this.ApiTargets.BaseApi}/v1/image_studio/prompt`;
|
||||
// for now ...
|
||||
|
||||
const uuidIdempotencyToken = crypto.randomUUID(); // Generate a new UUID
|
||||
const body = {
|
||||
uuid_idempotency_token: uuidIdempotencyToken,
|
||||
disable_system_prompt: disableSystemPrompt,
|
||||
prompt,
|
||||
snapshot_media_token: snapshotMediaToken, // Changed from scene_media_token to snapshot_media_token
|
||||
additional_images: additionalImages,
|
||||
@@ -108,6 +111,7 @@ export class EngineApi extends ApiManager {
|
||||
const postResponse = await this.post<
|
||||
{
|
||||
uuid_idempotency_token: string;
|
||||
disable_system_prompt: boolean;
|
||||
prompt: string;
|
||||
snapshot_media_token: string;
|
||||
additional_images?: string[];
|
||||
|
||||
@@ -40,7 +40,7 @@ export class JobsApi extends ApiManager {
|
||||
|
||||
public ListRecentJobs(): Promise<ApiResponse<Job[]>> {
|
||||
const endpoint = `${this.ApiTargets.BaseApi}/v1/jobs/session`;
|
||||
|
||||
|
||||
return this.get<{
|
||||
success: boolean;
|
||||
jobs: Job[];
|
||||
|
||||
@@ -33,4 +33,5 @@ export interface JobStatusResponse {
|
||||
maybe_first_started_at: string;
|
||||
requires_keepalive: boolean;
|
||||
status: JobStatus;
|
||||
progress_percentage: number;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,160 @@ import { PopoverMenu } from "~/components/reusable/Popover/Popover";
|
||||
import { CompletedCard } from "./CompletedCard";
|
||||
import { InProgressCard } from "./InProgressCard";
|
||||
import { H3, P, Tooltip } from "~/components";
|
||||
import { activeImageGenerationJobs, userMovies } from "~/signals";
|
||||
import { activeImageGenerationJobs, toasts, userMovies } from "~/signals";
|
||||
import { useEffect, useState } from "react";
|
||||
import { JobsApi } from "~/Classes/ApiManager/JobsApi";
|
||||
import { toast } from "sonner";
|
||||
|
||||
// TODO ensure we de-dupe all this extra code.
|
||||
interface CompletedItem {
|
||||
token: string;
|
||||
maybe_title: string;
|
||||
public_bucket_path: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
interface ActiveJob {
|
||||
job_token: string;
|
||||
request: {
|
||||
maybe_model_title: string;
|
||||
};
|
||||
status: {
|
||||
status: string;
|
||||
progress_percentage: number;
|
||||
};
|
||||
}
|
||||
|
||||
// {
|
||||
// "job_token": "jinf_421nnr6rj8wy2sj0s11v9fns32s",
|
||||
// "request": {
|
||||
// "inference_category": "image_generation",
|
||||
// "maybe_model_type": "image_gen_api",
|
||||
// "maybe_model_token": null,
|
||||
// "maybe_model_title": "Image Generation",
|
||||
// "maybe_raw_inference_text": null,
|
||||
// "maybe_style_name": null,
|
||||
// "maybe_live_portrait_details": null,
|
||||
// "maybe_lipsync_details": null
|
||||
// },
|
||||
// "status": {
|
||||
// "status": "complete_success",
|
||||
// "maybe_extra_status_description": null,
|
||||
// "maybe_assigned_worker": "inference-job-4o-image-848c55d5dd-555kn",
|
||||
// "maybe_assigned_cluster": "storyteller-eks-1",
|
||||
// "maybe_first_started_at": "2025-04-15T04:41:01Z",
|
||||
// "maybe_current_execution_duration_seconds": null,
|
||||
// "attempt_count": 1,
|
||||
// "requires_keepalive": false,
|
||||
// "maybe_failure_category": null,
|
||||
// "progress_percentage": 100
|
||||
// },
|
||||
// "maybe_result": {
|
||||
// "entity_type": "media_file",
|
||||
// "entity_token": "m_ytrjp1fcd5tb43ebcs3ewhq6y05tfb",
|
||||
// "maybe_public_bucket_media_path": "/media/p/t/7/y/g/pt7yg2xfv4ynemssfv1mwb7yxe26e2nb/image_pt7yg2xfv4ynemssfv1mwb7yxe26e2nb.png",
|
||||
// "media_links": {
|
||||
// "cdn_url": "https://cdn-2.fakeyou.com/media/p/t/7/y/g/pt7yg2xfv4ynemssfv1mwb7yxe26e2nb/image_pt7yg2xfv4ynemssfv1mwb7yxe26e2nb.png",
|
||||
// "maybe_thumbnail_template": "https://cdn-2.fakeyou.com/cdn-cgi/image/width={WIDTH},quality=95/media/p/t/7/y/g/pt7yg2xfv4ynemssfv1mwb7yxe26e2nb/image_pt7yg2xfv4ynemssfv1mwb7yxe26e2nb.png",
|
||||
// "maybe_video_previews": null
|
||||
// },
|
||||
// "maybe_successfully_completed_at": "2025-04-15T04:42:14Z"
|
||||
// },
|
||||
// "created_at": "2025-04-15T04:41:01Z",
|
||||
// "updated_at": "2025-04-15T04:42:14Z"
|
||||
// }
|
||||
|
||||
export function Activity() {
|
||||
useSignals();
|
||||
const [completedItems, setCompletedItems] = useState<CompletedItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [jobs, setJobs] = useState<ActiveJob[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const pollData = async () => {
|
||||
const fetchJobs = async () => {
|
||||
try {
|
||||
const jobsApi = new JobsApi();
|
||||
const jobsResponse = await jobsApi.ListRecentJobs();
|
||||
|
||||
if (jobsResponse.success && jobsResponse.data) {
|
||||
// Process active jobs
|
||||
const activeJobs = jobsResponse.data
|
||||
.filter(job => {
|
||||
const status = job.status?.status?.toLowerCase();
|
||||
return status && status !== "complete_success" && status !== "failed";
|
||||
})
|
||||
.map(job => ({
|
||||
job_token: job.job_token,
|
||||
request: {
|
||||
maybe_model_title: job.request?.maybe_model_title || "Image Generation"
|
||||
},
|
||||
status: {
|
||||
status: job.status?.status?.toUpperCase() || "",
|
||||
progress_percentage: job.status?.progress_percentage || 0
|
||||
}
|
||||
}));
|
||||
|
||||
// Process completed items
|
||||
// Get completed items and filter out any that are already in our list
|
||||
const successfulJobs = jobsResponse.data
|
||||
.filter(job => job.status?.status?.toLowerCase() === "complete_success");
|
||||
|
||||
// Create a set of existing tokens for faster lookup
|
||||
const existingTokens = new Set(completedItems.map(item => item.token));
|
||||
|
||||
// Filter out jobs we already have in our list
|
||||
const newCompletedItems = successfulJobs
|
||||
.filter(job => !existingTokens.has(job.job_token))
|
||||
.map(job => ({
|
||||
token: job.job_token,
|
||||
maybe_title: job.request?.maybe_model_title || "Image Generation",
|
||||
public_bucket_path: job.maybe_result?.maybe_public_bucket_media_path || "",
|
||||
updated_at: job.updated_at || new Date().toISOString()
|
||||
}));
|
||||
|
||||
// Show toast notification for newly completed items
|
||||
if (newCompletedItems.length > 0) {
|
||||
const count = newCompletedItems.length;
|
||||
const message = count === 1
|
||||
? `${newCompletedItems[0].maybe_title} completed successfully`
|
||||
: `${count} images completed successfully`;
|
||||
toast.success(message);
|
||||
}
|
||||
|
||||
// Update completed items with a maximum limit of 50 items
|
||||
setCompletedItems(prev => {
|
||||
const combined = [...newCompletedItems, ...prev];
|
||||
// Remove duplicates based on token
|
||||
const uniqueItems = Array.from(
|
||||
new Map(combined.map(item => [item.token, item])).values()
|
||||
);
|
||||
// Sort by updated_at in descending order and limit to 50 items
|
||||
return uniqueItems
|
||||
.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())
|
||||
.slice(0, 50);
|
||||
});
|
||||
setJobs(activeJobs);
|
||||
setLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error("Error Fetching Recent Jobs");
|
||||
console.error("Failed to fetch recent jobs:", error);
|
||||
}
|
||||
};
|
||||
await fetchJobs();
|
||||
};
|
||||
|
||||
// Initial load
|
||||
// Initial call to pollData
|
||||
pollData();
|
||||
// Set up polling interval (every 5 seconds)
|
||||
const interval = setInterval(async () => {
|
||||
await pollData();
|
||||
}, 5000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Tooltip content="Activity" position="bottom" closeOnClick={true}>
|
||||
@@ -30,11 +180,10 @@ export function Activity() {
|
||||
>
|
||||
<div className="max-h-[480px] overflow-y-auto">
|
||||
{/* In progress */}
|
||||
{activeImageGenerationJobs.value &&
|
||||
activeImageGenerationJobs.value.length > 0 && (
|
||||
{jobs && jobs.length > 0 && (
|
||||
<div>
|
||||
{activeImageGenerationJobs.value.map((movieJob) => (
|
||||
<InProgressCard key={movieJob.job_token} movie={movieJob} />
|
||||
{jobs.map((job) => (
|
||||
<InProgressCard key={job.job_token} movie={job} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
@@ -62,11 +211,17 @@ export function Activity() {
|
||||
<div>
|
||||
{/* Completed */}
|
||||
<div className="flex flex-col">
|
||||
{userMovies.value?.map((movie) => (
|
||||
<CompletedCard key={movie.token} movie={movie} />
|
||||
{completedItems.map((item) => (
|
||||
<CompletedCard
|
||||
key={item.token}
|
||||
movie={item}
|
||||
setMovieId={(item_id) => {
|
||||
console.log("item_id", item_id);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PopoverMenu>
|
||||
|
||||
+17
-10
@@ -6,7 +6,7 @@ import { BucketConfig } from "~/api/BucketConfig";
|
||||
import dayjs from "dayjs";
|
||||
import environmentVariables from "~/Classes/EnvironmentVariables";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { downloadFile } from "~/pages/PageEnigma/comps/GenerateModals/utils/downloadFile";
|
||||
import { downloadFileImage } from "~/pages/PageEnigma/comps/GenerateModals/utils/downloadFile";
|
||||
import { styleList } from "~/pages/PageEnigma/styleList";
|
||||
import { GetCdnOrigin } from "~/api/GetCdnOrigin";
|
||||
|
||||
@@ -27,13 +27,20 @@ export function CompletedCard({ movie, setMovieId }: Props) {
|
||||
const bucketConfig = useRef<BucketConfig>(new BucketConfig());
|
||||
const [loadError, setLoadError] = useState(false);
|
||||
const cdnOrigin = GetCdnOrigin();
|
||||
const downloadLink = `https://${cdnOrigin}${movie.public_bucket_path}`;
|
||||
|
||||
const imageUrl = bucketConfig.current.getCdnUrl(
|
||||
movie.public_bucket_path + "-thumb.gif",
|
||||
360,
|
||||
20,
|
||||
);
|
||||
|
||||
const downloadLink = `${cdnOrigin}${movie.public_bucket_path}`;
|
||||
|
||||
// console.log(downloadLink);
|
||||
// console.log("This is the completed item", movie);
|
||||
// console.log("item completed card", movie);
|
||||
|
||||
// to do use the thumbnail image
|
||||
const imageUrl = downloadLink; // bucketConfig.current.getCdnUrl(
|
||||
// movie.public_bucket_path + "-thumb.gif",
|
||||
// 360,
|
||||
// 20,
|
||||
// );
|
||||
const styleName =
|
||||
movie.maybe_style_name && getStyleName(movie.maybe_style_name);
|
||||
|
||||
@@ -71,21 +78,21 @@ export function CompletedCard({ movie, setMovieId }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="pr-5">
|
||||
<div className="pr-5">
|
||||
<button
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const title =
|
||||
movie.maybe_title !== null ? movie.maybe_title : "Untitled";
|
||||
downloadFile({ url: downloadLink, title });
|
||||
downloadFileImage({ url: downloadLink, title });
|
||||
}}
|
||||
className="text-[15px] font-medium text-white/50 transition-all duration-150 hover:text-white/100"
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowDownToLine} className="mr-2" />
|
||||
Download
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+24
@@ -24,4 +24,28 @@ export const downloadFile = ({
|
||||
.catch(() => addToast(ToastTypes.ERROR, "Could not download file."));
|
||||
};
|
||||
|
||||
|
||||
export const downloadFileImage = ({
|
||||
url,
|
||||
title,
|
||||
}: {
|
||||
url: string;
|
||||
title: string;
|
||||
}) => {
|
||||
// console.log(url);
|
||||
fetch(url)
|
||||
.then((resp) => resp.blob())
|
||||
.then((blob) => {
|
||||
const blobUrl = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.style.display = "none";
|
||||
a.href = blobUrl;
|
||||
a.download = `${title}.png`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
})
|
||||
.catch(() => addToast(ToastTypes.ERROR, "Could not download file."));
|
||||
};
|
||||
|
||||
//check
|
||||
|
||||
@@ -430,8 +430,10 @@ export const PromptBox = () => {
|
||||
screenshot: snapshot.file,
|
||||
sceneMediaToken: "",
|
||||
});
|
||||
|
||||
|
||||
console.log("useSystemPrompt", useSystemPrompt);
|
||||
await engineApi.enqueueImageGeneration({
|
||||
disableSystemPrompt: !useSystemPrompt,
|
||||
prompt: prompt,
|
||||
snapshotMediaToken: snapshotResult.data || "",
|
||||
additionalImages: referenceImages.map((image) => image.mediaToken),
|
||||
|
||||
@@ -16,7 +16,7 @@ import normalizeCss from "./styles/normalize.css?url";
|
||||
import tailwindCss from "./styles/tailwind.css?url";
|
||||
import baseCss from "./styles/base.css?url";
|
||||
import { Environment, Configs } from "./configs";
|
||||
import { api } from "@storyteller/api";
|
||||
//import { api } from "@storyteller/api";
|
||||
|
||||
// The following import prevents a Font Awesome icon server-side rendering bug,
|
||||
// where the icons flash from a very large icon down to a properly sized one:
|
||||
@@ -73,7 +73,7 @@ export async function loader() {
|
||||
break;
|
||||
}
|
||||
|
||||
console.log(">>>> API LIBRARY IMPORT TEST", api());
|
||||
//console.log(">>>> API LIBRARY IMPORT TEST", api());
|
||||
|
||||
const env = {
|
||||
// @ts-expect-error ProvessEnv is correct
|
||||
|
||||
@@ -100,7 +100,6 @@
|
||||
"eslint-plugin-storybook": "^0.8.0",
|
||||
"husky": "^9.0.11",
|
||||
"lint-staged": "^15.2.2",
|
||||
"node-sass": "^9.0.0",
|
||||
"postcss": "^8.4.37",
|
||||
"postcss-import": "^16.0.1",
|
||||
"postcss-nesting": "^12.1.0",
|
||||
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
echo "Removing Node Modules..."
|
||||
rm -rf ./node_modules/
|
||||
rm -rf ./apps/editor2d/node_modules/
|
||||
rm -rf ./apps/editor3d/node_modules/
|
||||
|
||||
@@ -23,7 +23,7 @@ export default defineConfig(() => ({
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points.
|
||||
entry: 'src/index.ts',
|
||||
name: '@frontend/api',
|
||||
name: '@storyteller/api',
|
||||
fileName: 'index',
|
||||
// Change this to the formats you want to support.
|
||||
// Don't forget to update your package.json as well.
|
||||
|
||||
Generated
-1573
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,7 @@ RUN apt-get update \
|
||||
FROM ubuntu:jammy as rust-base
|
||||
|
||||
# NB: This can be "stable" or another version.
|
||||
ARG RUST_TOOLCHAIN="1.85.1"
|
||||
ARG RUST_TOOLCHAIN="1.86.0"
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ RUN apt-get update \
|
||||
# TODO(bt, 2023-02-23): This has not been verified to work yet.
|
||||
RUN export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
|
||||
|
||||
ARG RUST_TOOLCHAIN="1.81.0"
|
||||
ARG RUST_TOOLCHAIN="1.86.0"
|
||||
|
||||
# Install Rust
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
|
||||
| sh -s -- --default-toolchain $RUST_TOOLCHAIN -y
|
||||
|
||||
Reference in New Issue
Block a user