smooth-py 0.1.3.post0__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of smooth-py might be problematic. Click here for more details.
|
@@ -4,11 +4,11 @@ import asyncio
|
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
6
6
|
import time
|
|
7
|
+
import urllib.parse
|
|
7
8
|
from typing import (
|
|
8
9
|
Any,
|
|
9
10
|
Literal,
|
|
10
11
|
)
|
|
11
|
-
from urllib.parse import urlencode
|
|
12
12
|
|
|
13
13
|
import httpx
|
|
14
14
|
import requests
|
|
@@ -20,6 +20,20 @@ logger = logging.getLogger("smooth")
|
|
|
20
20
|
|
|
21
21
|
BASE_URL = "https://api2.circlemind.co/api/"
|
|
22
22
|
|
|
23
|
+
|
|
24
|
+
# --- Utils ---
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _encode_url(url: str, interactive: bool = True, embed: bool = False) -> str:
|
|
28
|
+
parsed_url = urllib.parse.urlparse(url)
|
|
29
|
+
params = urllib.parse.parse_qs(parsed_url.query)
|
|
30
|
+
params.update({
|
|
31
|
+
"interactive": "true" if interactive else "false",
|
|
32
|
+
"embed": "true" if embed else "false"
|
|
33
|
+
})
|
|
34
|
+
return urllib.parse.urlunparse(parsed_url._replace(query=urllib.parse.urlencode(params)))
|
|
35
|
+
|
|
36
|
+
|
|
23
37
|
# --- Models ---
|
|
24
38
|
# These models define the data structures for API requests and responses.
|
|
25
39
|
|
|
@@ -151,6 +165,20 @@ class BaseClient:
|
|
|
151
165
|
# --- Synchronous Client ---
|
|
152
166
|
|
|
153
167
|
|
|
168
|
+
class BrowserSessionHandle(BaseModel):
|
|
169
|
+
"""Browser session handle model."""
|
|
170
|
+
|
|
171
|
+
browser_session: BrowserSessionResponse = Field(description="The browser session associated with this handle.")
|
|
172
|
+
|
|
173
|
+
def session_id(self):
|
|
174
|
+
"""Returns the session ID for the browser session."""
|
|
175
|
+
return self.browser_session.session_id
|
|
176
|
+
|
|
177
|
+
def live_url(self, interactive: bool = True, embed: bool = False):
|
|
178
|
+
"""Returns the live URL for the browser session."""
|
|
179
|
+
return _encode_url(self.browser_session.live_url, interactive=interactive, embed=embed)
|
|
180
|
+
|
|
181
|
+
|
|
154
182
|
class TaskHandle:
|
|
155
183
|
"""A handle to a running task."""
|
|
156
184
|
|
|
@@ -182,20 +210,15 @@ class TaskHandle:
|
|
|
182
210
|
|
|
183
211
|
def live_url(self, interactive: bool = True, embed: bool = False, timeout: int | None = None):
|
|
184
212
|
"""Returns the live URL for the task."""
|
|
185
|
-
params = {
|
|
186
|
-
"interactive": interactive,
|
|
187
|
-
"embed": embed
|
|
188
|
-
}
|
|
189
|
-
|
|
190
213
|
if self._task_response and self._task_response.live_url:
|
|
191
|
-
return
|
|
214
|
+
return _encode_url(self._task_response.live_url, interactive=interactive, embed=embed)
|
|
192
215
|
|
|
193
216
|
start_time = time.time()
|
|
194
217
|
while timeout is None or (time.time() - start_time) < timeout:
|
|
195
218
|
task_response = self._client._get_task(self.id) # pyright: ignore [reportPrivateUsage]
|
|
196
219
|
self._task_response = task_response
|
|
197
220
|
if self._task_response.live_url:
|
|
198
|
-
return
|
|
221
|
+
return _encode_url(self._task_response.live_url, interactive=interactive, embed=embed)
|
|
199
222
|
time.sleep(1)
|
|
200
223
|
|
|
201
224
|
raise TimeoutError(f"Live URL not available for task {self.id}.")
|
|
@@ -312,7 +335,7 @@ class SmoothClient(BaseClient):
|
|
|
312
335
|
|
|
313
336
|
return TaskHandle(initial_response.id, self)
|
|
314
337
|
|
|
315
|
-
def open_session(self, session_id: str | None = None) ->
|
|
338
|
+
def open_session(self, session_id: str | None = None) -> BrowserSessionHandle:
|
|
316
339
|
"""Gets an interactive browser instance.
|
|
317
340
|
|
|
318
341
|
Args:
|
|
@@ -330,7 +353,7 @@ class SmoothClient(BaseClient):
|
|
|
330
353
|
json=BrowserSessionRequest(session_id=session_id).model_dump(exclude_none=True),
|
|
331
354
|
)
|
|
332
355
|
data = self._handle_response(response)
|
|
333
|
-
return BrowserSessionResponse(**data["r"])
|
|
356
|
+
return BrowserSessionHandle(browser_session=BrowserSessionResponse(**data["r"]))
|
|
334
357
|
except requests.exceptions.RequestException as e:
|
|
335
358
|
logger.error(f"Request failed: {e}")
|
|
336
359
|
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
@@ -396,19 +419,15 @@ class AsyncTaskHandle:
|
|
|
396
419
|
|
|
397
420
|
async def live_url(self, interactive: bool = True, embed: bool = False, timeout: int | None = None):
|
|
398
421
|
"""Returns the live URL for the task."""
|
|
399
|
-
params = {
|
|
400
|
-
"interactive": interactive,
|
|
401
|
-
"embed": embed
|
|
402
|
-
}
|
|
403
422
|
if self._task_response and self._task_response.live_url:
|
|
404
|
-
return
|
|
423
|
+
return _encode_url(self._task_response.live_url, interactive=interactive, embed=embed)
|
|
405
424
|
|
|
406
425
|
start_time = time.time()
|
|
407
426
|
while timeout is None or (time.time() - start_time) < timeout:
|
|
408
427
|
task_response = await self._client._get_task(self.id) # pyright: ignore [reportPrivateUsage]
|
|
409
428
|
self._task_response = task_response
|
|
410
429
|
if task_response.live_url is not None:
|
|
411
|
-
return
|
|
430
|
+
return _encode_url(self._task_response.live_url, interactive=interactive, embed=embed)
|
|
412
431
|
await asyncio.sleep(1)
|
|
413
432
|
|
|
414
433
|
raise TimeoutError(f"Live URL not available for task {self.id}.")
|
|
@@ -521,7 +540,7 @@ class SmoothAsyncClient(BaseClient):
|
|
|
521
540
|
initial_response = await self._submit_task(payload)
|
|
522
541
|
return AsyncTaskHandle(initial_response.id, self)
|
|
523
542
|
|
|
524
|
-
async def open_session(self, session_id: str | None = None) ->
|
|
543
|
+
async def open_session(self, session_id: str | None = None) -> BrowserSessionHandle:
|
|
525
544
|
"""Opens an interactive browser instance asynchronously.
|
|
526
545
|
|
|
527
546
|
Args:
|
|
@@ -539,7 +558,7 @@ class SmoothAsyncClient(BaseClient):
|
|
|
539
558
|
json=BrowserSessionRequest(session_id=session_id).model_dump(exclude_none=True),
|
|
540
559
|
)
|
|
541
560
|
data = self._handle_response(response)
|
|
542
|
-
return BrowserSessionResponse(**data["r"])
|
|
561
|
+
return BrowserSessionHandle(browser_session=BrowserSessionResponse(**data["r"]))
|
|
543
562
|
except httpx.RequestError as e:
|
|
544
563
|
logger.error(f"Request failed: {e}")
|
|
545
564
|
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
File without changes
|