1use serde::{Deserialize, Serialize};
2
3use crate::client::Client;
4use crate::error::Result;
5use crate::jobs::{JobCreateRequest, JobCreateResponse};
6
7#[derive(Debug, Clone, Serialize, Default)]
13pub struct ScrapeTarget {
14 pub name: String,
16
17 pub url: String,
19
20 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
22 pub target_type: Option<String>,
23
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub selector: Option<String>,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub content: Option<String>,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub notebook: Option<String>,
35
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub recursive: Option<bool>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub max_pages: Option<i32>,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub delay_ms: Option<i32>,
47
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub ingest: Option<String>,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub spec_url: Option<String>,
55}
56
57#[derive(Debug, Clone, Serialize, Default)]
59pub struct ScrapeRequest {
60 pub targets: Vec<ScrapeTarget>,
62}
63
64#[derive(Debug, Clone, Deserialize)]
66pub struct ScrapeResponse {
67 pub job_id: String,
69
70 #[serde(default)]
72 pub status: String,
73
74 #[serde(default)]
76 pub targets: i32,
77
78 #[serde(default)]
80 pub request_id: String,
81}
82
83#[derive(Debug, Clone, Serialize, Default)]
89pub struct ScreenshotURL {
90 pub url: String,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub width: Option<i32>,
96
97 #[serde(skip_serializing_if = "Option::is_none")]
99 pub height: Option<i32>,
100
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub full_page: Option<bool>,
104
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub delay_ms: Option<i32>,
108}
109
110#[derive(Debug, Clone, Serialize, Default)]
112pub struct ScreenshotRequest {
113 pub urls: Vec<ScreenshotURL>,
115}
116
117#[derive(Debug, Clone, Deserialize)]
119pub struct ScreenshotResult {
120 pub url: String,
122
123 #[serde(default)]
125 pub base64: String,
126
127 #[serde(default)]
129 pub format: String,
130
131 #[serde(default)]
133 pub width: i32,
134
135 #[serde(default)]
137 pub height: i32,
138
139 #[serde(default)]
141 pub error: Option<String>,
142}
143
144#[derive(Debug, Clone, Deserialize)]
146pub struct ScreenshotResponse {
147 #[serde(default)]
149 pub screenshots: Vec<ScreenshotResult>,
150
151 #[serde(default)]
153 pub count: i32,
154}
155
156#[derive(Debug, Clone, Deserialize)]
158pub struct ScreenshotJobResponse {
159 pub job_id: String,
161
162 #[serde(default)]
164 pub status: String,
165
166 #[serde(default)]
168 pub urls: i32,
169
170 #[serde(default)]
172 pub request_id: String,
173}
174
175impl Client {
180 pub async fn scrape(&self, req: &ScrapeRequest) -> Result<ScrapeResponse> {
182 let (resp, _meta) = self
183 .post_json::<ScrapeRequest, ScrapeResponse>("/qai/v1/scraper/scrape", req)
184 .await?;
185 Ok(resp)
186 }
187
188 pub async fn screenshot(&self, req: &ScreenshotRequest) -> Result<ScreenshotResponse> {
191 let (resp, _meta) = self
192 .post_json::<ScreenshotRequest, ScreenshotResponse>("/qai/v1/scraper/screenshot", req)
193 .await?;
194 Ok(resp)
195 }
196
197 pub async fn screenshot_job(&self, req: &ScreenshotRequest) -> Result<JobCreateResponse> {
199 let params = serde_json::to_value(req)?;
200 self.create_job(&JobCreateRequest {
201 job_type: "screenshot".into(),
202 params,
203 })
204 .await
205 }
206}