mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
post id
This commit is contained in:
1 parent
53e9f1c463
commit
893a29f965
8 files changed
+140
-37
No files matched your search
@@ -1,5 +1,6 @@
|
||||
pub mod baggage;
|
||||
pub mod file_id;
|
||||
pub mod post_id;
|
||||
pub mod request_id;
|
||||
pub mod sentry_trace;
|
||||
pub mod svg_path_data;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
/// Type for Post IDs.
|
||||
/// Post IDs are UUIds.
|
||||
/// These are often shared with media/image uploads' `FileId`s, but seldom video generation `FileId`s
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PostId(pub String);
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::credentials::grok_full_credentials::GrokFullCredentials;
|
||||
use crate::datatypes::api::file_id::FileId;
|
||||
use crate::datatypes::api::post_id::PostId;
|
||||
use crate::datatypes::file_upload_spec::FileUploadSpec;
|
||||
use crate::error::grok_error::GrokError;
|
||||
use crate::error::grok_generic_api_error::GrokGenericApiError;
|
||||
@@ -19,17 +20,45 @@ pub struct UploadImageAndGenerateVideo<'a, P: AsRef<Path>> {
|
||||
// NB: Must be owned.
|
||||
pub file: FileUploadSpec<P>,
|
||||
|
||||
/// Video generation prompt
|
||||
pub prompt: Option<&'a str>,
|
||||
|
||||
/// Wait for the full video to be generated before returning
|
||||
/// By default, the endpoint stays open for 30-ish seconds while
|
||||
/// the video generates and streams back JSON progress updates.
|
||||
/// If we set this to false, we'll wait for the video ID and exit
|
||||
/// the request early, before the video is finished asynchronously
|
||||
/// generating.
|
||||
pub wait_for_generation: bool,
|
||||
|
||||
pub individual_request_timeout: Option<Duration>,
|
||||
|
||||
}
|
||||
|
||||
pub struct ImageUploadResult {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImageUploadAndGenerateVideoResult {
|
||||
/// The ID of the "post"
|
||||
/// This often is equivalent to the upload id / `image_file_id`,
|
||||
/// and this is in the user-facing URL for website navigation to the video.
|
||||
pub post_id: PostId,
|
||||
|
||||
/// The ID of the file we uploaded
|
||||
pub image_file_id: FileId,
|
||||
|
||||
/// The ID of the video we're generating, which may or may not exist yet.
|
||||
pub video_file_id: Option<FileId>,
|
||||
|
||||
/// The URL for the video, which may or may not exist yet.
|
||||
pub video_url: Option<String>,
|
||||
|
||||
/// Whether the video generation has already completed.
|
||||
/// Based on request `wait_for_generation`.
|
||||
pub generation_is_complete: bool,
|
||||
}
|
||||
|
||||
pub async fn upload_image_and_generate_video<P: AsRef<Path>>(args: UploadImageAndGenerateVideo<'_, P>) -> Result<ImageUploadResult, GrokError> {
|
||||
pub async fn upload_image_and_generate_video<P: AsRef<Path>>(args: UploadImageAndGenerateVideo<'_, P>)
|
||||
-> Result<ImageUploadAndGenerateVideoResult, GrokError>
|
||||
{
|
||||
|
||||
info!("Uploading file to Grok...");
|
||||
|
||||
@@ -44,7 +73,7 @@ pub async fn upload_image_and_generate_video<P: AsRef<Path>>(args: UploadImageAn
|
||||
info!("File URI: {:?}", upload_result.file_uri);
|
||||
info!("File Metadata ID: {:?}", upload_result.file_id);
|
||||
|
||||
let upload_file_id = match &upload_result.file_id {
|
||||
let upload_file_id = match upload_result.file_id {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
error!("Previous file upload failed. Cannot continue.");
|
||||
@@ -63,13 +92,13 @@ pub async fn upload_image_and_generate_video<P: AsRef<Path>>(args: UploadImageAn
|
||||
|
||||
let request = GrokCreateMediaPost {
|
||||
user_id: &args.full_credentials.client_secrets.user_id,
|
||||
file_id: upload_file_id,
|
||||
file_id: &upload_file_id,
|
||||
media_type: MediaPostType::UserUploadedImage,
|
||||
cookie: args.full_credentials.cookies.as_str(),
|
||||
request_timeout: args.individual_request_timeout,
|
||||
};
|
||||
|
||||
let _post_result = request.send().await?;
|
||||
let post_result = request.send().await?;
|
||||
|
||||
info!("Posted Image.");
|
||||
|
||||
@@ -89,9 +118,20 @@ pub async fn upload_image_and_generate_video<P: AsRef<Path>>(args: UploadImageAn
|
||||
numbers: &args.full_credentials.client_secrets.numbers,
|
||||
};
|
||||
|
||||
let video_gen_result = request.stream_only_video_id().await?;
|
||||
let maybe_video_file_id;
|
||||
let generation_is_complete;
|
||||
|
||||
let maybe_video_file_id = video_gen_result.video_file_id;
|
||||
if args.wait_for_generation {
|
||||
info!("Waiting for video generation...");
|
||||
let video_gen_result = request.send().await?;
|
||||
maybe_video_file_id = video_gen_result.video_file_id;
|
||||
generation_is_complete = true;
|
||||
} else {
|
||||
info!("Streaming response partially...");
|
||||
let video_gen_result = request.stream_only_video_id().await?;
|
||||
maybe_video_file_id = video_gen_result.video_file_id;
|
||||
generation_is_complete = false;
|
||||
}
|
||||
|
||||
let maybe_video_url = maybe_video_file_id
|
||||
.as_ref()
|
||||
@@ -118,9 +158,12 @@ pub async fn upload_image_and_generate_video<P: AsRef<Path>>(args: UploadImageAn
|
||||
|
||||
info!("Media Liked");
|
||||
|
||||
Ok(ImageUploadResult {
|
||||
Ok(ImageUploadAndGenerateVideoResult {
|
||||
post_id: post_result.post_id,
|
||||
image_file_id: upload_file_id,
|
||||
video_file_id: maybe_video_file_id,
|
||||
video_url: maybe_video_url,
|
||||
generation_is_complete,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -145,23 +188,23 @@ mod tests {
|
||||
async fn test_upload_image_and_generate_video() -> AnyhowResult<()> {
|
||||
setup_test_logging(LevelFilter::Info);
|
||||
|
||||
//let image_path = "/Users/bt/Pictures/Zelda 64 Art/FCgYX6tWEAEhpsy.jpg";
|
||||
//let prompt = "our hero link plunges the sword into the pedestal, the temple is glowing with a blue aura";
|
||||
|
||||
let image_path = "/Users/bt/Pictures/Midjourney/hipster_girl.png";
|
||||
let maybe_prompt = Some("an anime girl runs away from a giant t-rex");
|
||||
|
||||
let cookies = get_typed_test_cookies()?;
|
||||
|
||||
let secrets = request_client_secrets(RequestClientSecretsArgs {
|
||||
cookies: &cookies,
|
||||
}).await?;
|
||||
|
||||
//let image_path = "/Users/bt/Pictures/Zelda 64 Art/FCgYX6tWEAEhpsy.jpg";
|
||||
//let prompt = "our hero link plunges the sword into the pedestal, the temple is glowing with a blue aura";
|
||||
|
||||
let image_path = "/Users/bt/Pictures/Wallpaper/digital_topography_midjourney.png";
|
||||
let maybe_prompt = Some("the mountains are digitial and lights flicker up and down the topography. the camera pans gently by");
|
||||
|
||||
println!("Verification Token: {:?}", secrets.verification_token);
|
||||
println!("Sentry Trace: {:?}", secrets.sentry_trace);
|
||||
println!("Numbers: {:?}", secrets.numbers);
|
||||
println!("Svg Path: {:?}", secrets.svg_path_data);
|
||||
println!("Baggage: {:?}", secrets.baggage);
|
||||
println!("[test] Verification Token: {:?}", secrets.verification_token);
|
||||
println!("[test] Sentry Trace: {:?}", secrets.sentry_trace);
|
||||
println!("[test] Numbers: {:?}", secrets.numbers);
|
||||
println!("[test] Svg Path: {:?}", secrets.svg_path_data);
|
||||
println!("[test] Baggage: {:?}", secrets.baggage);
|
||||
|
||||
let credentials = GrokFullCredentials::from_cookies_and_client_secrets(cookies, secrets);
|
||||
|
||||
@@ -170,10 +213,13 @@ mod tests {
|
||||
file: FileUploadSpec::Path(image_path),
|
||||
prompt: maybe_prompt,
|
||||
individual_request_timeout: None,
|
||||
wait_for_generation: false,
|
||||
}).await?;
|
||||
|
||||
println!("Video File ID: {:?}", video_result.video_file_id);
|
||||
println!("Video URL: {:?}", video_result.video_url);
|
||||
println!("[test] Post ID: {:?}", video_result.post_id);
|
||||
println!("[test] Image File ID: {:?}", video_result.image_file_id);
|
||||
println!("[test] Video File ID: {:?}", video_result.video_file_id);
|
||||
println!("[test] Video URL: {:?}", video_result.video_url);
|
||||
|
||||
assert_eq!(1, 2);
|
||||
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"post": {
|
||||
"id": "3c896963-8e6b-4e71-83e1-4253b2bf2ac8",
|
||||
"userId":"85980643-ffab-4984-a3de-59a608c47d7f",
|
||||
"createTime":"2025-10-30T23:22:19.528702688Z",
|
||||
"prompt":"",
|
||||
"mediaType":"MEDIA_POST_TYPE_IMAGE",
|
||||
"mediaUrl":"https://assets.grok.com/users/85980643-ffab-4984-a3de-59a608c47d7f/3c896963-8e6b-4e71-83e1-4253b2bf2ac8/content",
|
||||
"mimeType":"image/jpeg",
|
||||
"audioUrls":[],
|
||||
"childPosts":[]
|
||||
}
|
||||
}
|
||||
+21
-15
@@ -1,10 +1,12 @@
|
||||
use crate::client::browser_user_agents::FIREFOX_143_MAC_USER_AGENT;
|
||||
use crate::datatypes::api::file_id::FileId;
|
||||
use crate::datatypes::api::post_id::PostId;
|
||||
use crate::datatypes::api::user_id::UserId;
|
||||
use crate::error::grok_client_error::GrokClientError;
|
||||
use crate::error::grok_error::GrokError;
|
||||
use crate::error::grok_generic_api_error::GrokGenericApiError;
|
||||
use crate::requests::create_media_post::request::CreateMediaPostWireRequest;
|
||||
use crate::requests::create_media_post::response::CreateMediaPostRawResponse;
|
||||
use crate::requests::upload_file::grok_upload_file::{GrokUploadFile, GrokUploadFileResponse};
|
||||
use crate::utils::user_and_file_id_to_image_url::user_and_file_id_to_image_url;
|
||||
use log::{error, info};
|
||||
@@ -32,8 +34,9 @@ pub enum MediaPostType {
|
||||
}
|
||||
|
||||
/// Response type
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GrokCreateMediaPostResponse {
|
||||
pub post_id: PostId,
|
||||
}
|
||||
|
||||
impl <'a> GrokCreateMediaPost<'a> {
|
||||
@@ -47,9 +50,17 @@ impl <'a> GrokCreateMediaPost<'a> {
|
||||
|
||||
// TODO: Headers were from Chromium, not Firefox. Partial implementation.
|
||||
let mut request_builder = client.post(CREATE_MEDIA_POST_URL)
|
||||
// TODO: Missing header "baggage"
|
||||
// TODO: Missing header "sentry-trace"
|
||||
// TODO: Missing header "traceparent"
|
||||
// TODO: Missing header "traceparent"
|
||||
// TODO: Missing header "x-statsig-id"
|
||||
// TODO: Missing header "x-xai-request-id"
|
||||
//.header("sec-ch-ua", "") // NB: Not present in firefox
|
||||
//.header("sec-ch-ua-mobile", "") // NB: Not present in firefox
|
||||
//.header("sec-ch-ua-platform", "") // NB: Not present in firefox
|
||||
.header(ACCEPT, "*/*")
|
||||
.header(ACCEPT_LANGUAGE, "en-US,en;q=0.5")
|
||||
// TODO: Missing header "baggage"
|
||||
.header(CACHE_CONTROL, "no-cache")
|
||||
.header(CONTENT_TYPE, "application/json")
|
||||
.header(COOKIE, self.cookie.to_string())
|
||||
@@ -57,17 +68,9 @@ impl <'a> GrokCreateMediaPost<'a> {
|
||||
.header(PRAGMA, "no-cache")
|
||||
.header("priority", "u=1, i")
|
||||
.header(REFERER, "https://grok.com/imagine/favorites")
|
||||
//.header("sec-ch-ua", "") // TODO
|
||||
//.header("sec-ch-ua-mobile", "") // TODO
|
||||
//.header("sec-ch-ua-platform", "") // TODO
|
||||
.header("sec-fetch-dest", "empty")
|
||||
.header("sec-fetch-mode", "cors")
|
||||
.header("sec-fetch-site", "same-origin")
|
||||
// TODO: Missing header "sentry-trace"
|
||||
// TODO: Missing header "traceparent"
|
||||
// TODO: Missing header "traceparent"
|
||||
// TODO: Missing header "x-statsig-id"
|
||||
// TODO: Missing header "x-xai-request-id"
|
||||
.header(USER_AGENT, FIREFOX_143_MAC_USER_AGENT);
|
||||
|
||||
//info!("Sending...");
|
||||
@@ -106,23 +109,24 @@ impl <'a> GrokCreateMediaPost<'a> {
|
||||
|
||||
let status = response.status();
|
||||
|
||||
let response_body = &response.text()
|
||||
let response_body = response.text()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error reading Grok create media response body: {:?}", err);
|
||||
GrokGenericApiError::WreqError(err)
|
||||
})?;
|
||||
|
||||
// TODO:
|
||||
// TODO: Handle errors (Cloudflare, Grok, etc.)
|
||||
if !status.is_success() {
|
||||
error!("Upload file request returned an error (code {}) : {:?}", status.as_u16(), response_body);
|
||||
//return Err(classify_general_http_status_code_and_body(status, response_body));
|
||||
}
|
||||
|
||||
//let response : GrokApiUploadFileResponse = serde_json::from_str(response_body)
|
||||
// .map_err(|err| GrokGenericApiError::SerdeResponseParseErrorWithBody(err, response_body.to_string()))?;
|
||||
let response : CreateMediaPostRawResponse = serde_json::from_str(&response_body)
|
||||
.map_err(|err| GrokGenericApiError::SerdeResponseParseErrorWithBody(err, response_body.to_string()))?;
|
||||
|
||||
Ok(GrokCreateMediaPostResponse{
|
||||
Ok(GrokCreateMediaPostResponse {
|
||||
post_id: PostId(response.post.id),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -153,6 +157,8 @@ mod tests {
|
||||
let result = request.send().await?;
|
||||
|
||||
println!("Result: {:?}", result);
|
||||
println!("Post ID: {:?}", result.post_id);
|
||||
|
||||
assert_eq!(1, 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod grok_create_media_post;
|
||||
pub (super) mod request;
|
||||
pub (super) mod request;
|
||||
mod response;
|
||||
@@ -0,0 +1,28 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CreateMediaPostRawResponse {
|
||||
pub post: PostData,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PostData {
|
||||
/// The identifier for the post
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"post": {
|
||||
"id": "3c896963-8e6b-4e71-83e1-4253b2bf2ac8",
|
||||
"userId":"85980643-ffab-4984-a3de-59a608c47d7f",
|
||||
"createTime":"2025-10-30T23:22:19.528702688Z",
|
||||
"prompt":"",
|
||||
"mediaType":"MEDIA_POST_TYPE_IMAGE",
|
||||
"mediaUrl":"https://assets.grok.com/users/85980643-ffab-4984-a3de-59a608c47d7f/3c896963-8e6b-4e71-83e1-4253b2bf2ac8/content",
|
||||
"mimeType":"image/jpeg",
|
||||
"audioUrls":[],
|
||||
"childPosts":[]
|
||||
}
|
||||
}
|
||||
*/
|
||||
+2
@@ -110,6 +110,7 @@ impl <'a> GrokVideoGenChatConversationBuilder<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
pub async fn send(&self) -> Result<GrokVideoGenChatConversationResponse, GrokError> {
|
||||
info!("Configuring client and request...");
|
||||
let client = Self::build_client()?;
|
||||
@@ -164,6 +165,7 @@ impl <'a> GrokVideoGenChatConversationBuilder<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fn build_client() -> Result<Client, GrokClientError> {
|
||||
Ok(Client::builder()
|
||||
.emulation(Emulation::Firefox143)
|
||||
|
||||
Reference in new issue
Block a user