@champpaba/gslide 0.1.4 → 0.1.5
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.
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/gslide/__init__.py +1 -1
- package/src/gslide/__pycache__/__init__.cpython-313.pyc +0 -0
- package/src/gslide/__pycache__/auth.cpython-313.pyc +0 -0
- package/src/gslide/__pycache__/gen.cpython-313.pyc +0 -0
- package/src/gslide/auth.py +8 -11
- package/src/gslide/gen.py +17 -47
package/package.json
CHANGED
package/pyproject.toml
CHANGED
package/src/gslide/__init__.py
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/gslide/auth.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"""Authentication session management — file I/O and browser operations."""
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
3
|
import sys
|
|
5
4
|
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
7
5
|
|
|
8
6
|
import click
|
|
9
7
|
|
|
@@ -18,10 +16,13 @@ def is_logged_in() -> bool:
|
|
|
18
16
|
return get_storage_path().exists()
|
|
19
17
|
|
|
20
18
|
|
|
21
|
-
def
|
|
22
|
-
path
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
def require_login() -> Path:
|
|
20
|
+
"""Return storage path or exit if not logged in."""
|
|
21
|
+
storage_path = get_storage_path()
|
|
22
|
+
if not storage_path.exists():
|
|
23
|
+
click.echo("Not logged in. Run: gslide auth login", err=True)
|
|
24
|
+
sys.exit(1)
|
|
25
|
+
return storage_path
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
def delete_storage_state() -> None:
|
|
@@ -56,11 +57,7 @@ def login() -> None:
|
|
|
56
57
|
|
|
57
58
|
def status() -> None:
|
|
58
59
|
"""Check if saved session is still valid."""
|
|
59
|
-
path =
|
|
60
|
-
|
|
61
|
-
if not path.exists():
|
|
62
|
-
click.echo("Not logged in. Run: gslide auth login")
|
|
63
|
-
sys.exit(1)
|
|
60
|
+
path = require_login()
|
|
64
61
|
|
|
65
62
|
from gslide.browser import BrowserSession
|
|
66
63
|
|
package/src/gslide/gen.py
CHANGED
|
@@ -16,7 +16,7 @@ import time
|
|
|
16
16
|
import click
|
|
17
17
|
from playwright.sync_api import Page, TimeoutError as PwTimeout
|
|
18
18
|
|
|
19
|
-
from gslide.auth import
|
|
19
|
+
from gslide.auth import require_login
|
|
20
20
|
from gslide.browser import BrowserSession
|
|
21
21
|
from gslide.prompts import PromptsData
|
|
22
22
|
|
|
@@ -73,9 +73,7 @@ def _reopen_panel(page: Page, retries: int = 2) -> None:
|
|
|
73
73
|
"""Reopen the HMV panel after insert overlay is dismissed."""
|
|
74
74
|
for attempt in range(retries + 1):
|
|
75
75
|
try:
|
|
76
|
-
|
|
77
|
-
hmv.click()
|
|
78
|
-
page.wait_for_selector('[role="tab"]', timeout=PANEL_LOAD_TIMEOUT_MS)
|
|
76
|
+
open_panel(page)
|
|
79
77
|
return
|
|
80
78
|
except (PwTimeout, Exception):
|
|
81
79
|
if attempt < retries:
|
|
@@ -92,11 +90,9 @@ def select_tab(page: Page, tab_name: str) -> None:
|
|
|
92
90
|
|
|
93
91
|
|
|
94
92
|
def _find_visible_textarea(page: Page) -> object:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
ta
|
|
98
|
-
if ta.is_visible():
|
|
99
|
-
return ta
|
|
93
|
+
ta = page.locator("textarea").first
|
|
94
|
+
if ta.is_visible():
|
|
95
|
+
return ta
|
|
100
96
|
raise GenerationError("No visible text input found in panel")
|
|
101
97
|
|
|
102
98
|
|
|
@@ -201,18 +197,11 @@ def _click_insert_button(page: Page, text: str) -> None:
|
|
|
201
197
|
raise GenerationError(f"'{text}' button not found after clicking preview")
|
|
202
198
|
|
|
203
199
|
|
|
204
|
-
def
|
|
205
|
-
"""Click preview then 'Insert on new slide'
|
|
206
|
-
_click_preview_image(page)
|
|
207
|
-
_click_insert_button(page, "Insert on new slide")
|
|
208
|
-
_wait_for_new_slide(page)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
def insert_slide(page: Page) -> None:
|
|
212
|
-
"""Click preview then 'Insert on new slide' for slide tab."""
|
|
200
|
+
def _insert_on_new_slide(page: Page) -> None:
|
|
201
|
+
"""Click preview then 'Insert on new slide' (used by both infographic and slide tabs)."""
|
|
213
202
|
_click_preview_image(page)
|
|
214
203
|
_click_insert_button(page, "Insert on new slide")
|
|
215
|
-
|
|
204
|
+
page.wait_for_timeout(2000)
|
|
216
205
|
|
|
217
206
|
|
|
218
207
|
def insert_image(page: Page, insert_as: str = "image") -> None:
|
|
@@ -221,16 +210,11 @@ def insert_image(page: Page, insert_as: str = "image") -> None:
|
|
|
221
210
|
|
|
222
211
|
option_text = "Insert as background" if insert_as == "background" else "Insert as image"
|
|
223
212
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
else:
|
|
213
|
+
try:
|
|
214
|
+
_click_insert_button(page, option_text)
|
|
215
|
+
except GenerationError:
|
|
228
216
|
# Fallback: try "Insert on new slide"
|
|
229
|
-
|
|
230
|
-
if fallback.count() > 0 and fallback.first.is_visible():
|
|
231
|
-
fallback.first.click()
|
|
232
|
-
else:
|
|
233
|
-
raise GenerationError(f"'{option_text}' button not found")
|
|
217
|
+
_click_insert_button(page, "Insert on new slide")
|
|
234
218
|
|
|
235
219
|
page.wait_for_timeout(2000)
|
|
236
220
|
|
|
@@ -240,29 +224,16 @@ def check_url(page: Page, presentation_id: str) -> None:
|
|
|
240
224
|
raise GenerationError("Browser navigated away from target presentation")
|
|
241
225
|
|
|
242
226
|
|
|
243
|
-
def _wait_for_new_slide(page: Page) -> None:
|
|
244
|
-
page.wait_for_timeout(2000)
|
|
245
|
-
|
|
246
|
-
|
|
247
227
|
# --- Orchestration ---
|
|
248
228
|
|
|
249
229
|
|
|
250
230
|
_INSERT_FN = {
|
|
251
|
-
"infographic": lambda page, **_:
|
|
252
|
-
"slide": lambda page, **_:
|
|
231
|
+
"infographic": lambda page, **_: _insert_on_new_slide(page),
|
|
232
|
+
"slide": lambda page, **_: _insert_on_new_slide(page),
|
|
253
233
|
"image": lambda page, **opts: insert_image(page, opts.get("insert_as", "image")),
|
|
254
234
|
}
|
|
255
235
|
|
|
256
236
|
|
|
257
|
-
def _require_login() -> "Path":
|
|
258
|
-
"""Return storage path or exit if not logged in."""
|
|
259
|
-
storage_path = get_storage_path()
|
|
260
|
-
if not storage_path.exists():
|
|
261
|
-
click.echo("Not logged in. Run: gslide auth login", err=True)
|
|
262
|
-
sys.exit(1)
|
|
263
|
-
return storage_path
|
|
264
|
-
|
|
265
|
-
|
|
266
237
|
def gen_single(
|
|
267
238
|
presentation_id: str,
|
|
268
239
|
tab: str,
|
|
@@ -273,7 +244,7 @@ def gen_single(
|
|
|
273
244
|
insert_as: str = "image",
|
|
274
245
|
) -> None:
|
|
275
246
|
"""Generate a single slide/infographic/image via browser automation."""
|
|
276
|
-
storage_path =
|
|
247
|
+
storage_path = require_login()
|
|
277
248
|
timeout_ms = timeout * 1000
|
|
278
249
|
|
|
279
250
|
with BrowserSession(storage_state=storage_path) as context:
|
|
@@ -281,7 +252,6 @@ def gen_single(
|
|
|
281
252
|
|
|
282
253
|
try:
|
|
283
254
|
navigate_to_presentation(page, presentation_id)
|
|
284
|
-
check_url(page, presentation_id)
|
|
285
255
|
|
|
286
256
|
if tab == "image" and slide_index is not None:
|
|
287
257
|
_navigate_to_slide(page, slide_index)
|
|
@@ -328,7 +298,7 @@ def gen_batch(
|
|
|
328
298
|
timeout: int = 120,
|
|
329
299
|
) -> None:
|
|
330
300
|
"""Generate all slides from prompts data in a single browser session."""
|
|
331
|
-
storage_path =
|
|
301
|
+
storage_path = require_login()
|
|
332
302
|
timeout_ms = timeout * 1000
|
|
333
303
|
total_slides = len(prompts_data.slides)
|
|
334
304
|
total_images = len(prompts_data.images)
|
|
@@ -421,5 +391,5 @@ def gen_batch(
|
|
|
421
391
|
for idx, tab, err in errors:
|
|
422
392
|
click.echo(f" #{idx} ({tab}): {err}")
|
|
423
393
|
click.echo(
|
|
424
|
-
f"Presentation:
|
|
394
|
+
f"Presentation: {PRESENTATION_URL.format(id=prompts_data.presentation_id)}"
|
|
425
395
|
)
|