From f3076c52ce7c801995ba489ac95919ea90aa73d6 Mon Sep 17 00:00:00 2001 From: Mantao Huang Date: Mon, 9 Mar 2026 12:21:45 -0400 Subject: [PATCH] Fix Zotero save completion handling --- zotero_automator.py | 44 ++++++++++++-------------------------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/zotero_automator.py b/zotero_automator.py index 69b4890..d2251e7 100644 --- a/zotero_automator.py +++ b/zotero_automator.py @@ -104,8 +104,7 @@ async def save_to_zotero(url, headless_mode="new"): else: assert worker is not None print("[*] Triggering save via extension service worker evaluation...") - # We wrap the call in a try/catch and return a structured object so we can extract the sessionID - session_id = await worker.evaluate('''async () => { + save_result = await worker.evaluate('''async () => { try { let tabs = await chrome.tabs.query({ active: true, currentWindow: true }); if (!tabs || tabs.length === 0) return {error: "No active tab found."}; @@ -113,11 +112,11 @@ async def save_to_zotero(url, headless_mode="new"): let tabInfo = Zotero.Connector_Browser.getTabInfo(tab.id); if (tabInfo && tabInfo.translators && tabInfo.translators.length) { - await Zotero.Connector_Browser.saveWithTranslator(tab, 0, {fallbackOnFailure: true}); - return { sessionID: tabInfo.instanceID }; // usually acts as sessionID + let result = await Zotero.Connector_Browser.saveWithTranslator(tab, 0, {fallbackOnFailure: true}); + return { ok: true, mode: "translator", result }; } else if (tabInfo) { - await Zotero.Connector_Browser.saveAsWebpage(tab, tabInfo.frameId, { snapshot: true }); - return { sessionID: tabInfo.instanceID }; + let result = await Zotero.Connector_Browser.saveAsWebpage(tab, tabInfo.frameId, { snapshot: true }); + return { ok: true, mode: "webpage", result }; } else { return {error: "No translator or webpage saving options available."}; } @@ -126,33 +125,14 @@ async def save_to_zotero(url, headless_mode="new"): } }''') - if not session_id or "error" in session_id: - print(f"[!] Save trigger failed: {session_id.get('error') if session_id else 'Unknown error'}") + if not save_result or "error" in save_result: + print(f"[!] Save trigger failed: {save_result.get('error') if save_result else 'Unknown error'}") else: - sid = session_id.get("sessionID") - print(f"[*] Save triggered (Session ID: {sid}). Polling for progress...") - - # Poll for completion - max_polls = 60 - for _ in range(max_polls): - await asyncio.sleep(1) - progress = await worker.evaluate('''async (sid) => { - try { - if (!Zotero.Connector) return {done: false, error: "Zotero.Connector uninitialized"}; - let res = await Zotero.Connector.callMethod("sessionProgress", { sessionID: sid }); - return res || {done: false}; - } catch(e) { - return {done: true, error: e.message}; // typically throws when it's totally done/cleaned up - } - }''', sid) - - if progress and progress.get("error"): - # Normally, when the progress tracker cleans up, callMethod("sessionProgress") throws - print(f"[*] Save completed or session ended. Error: {progress.get('error')}") - break - if progress and progress.get("done"): - print("[*] Translation and save processes finished successfully.") - break + save_mode = save_result.get("mode", "unknown") + returned = save_result.get("result") + print(f"[*] Save completed successfully via {save_mode}.") + if returned is not None: + print(f"[*] Save returned: {returned}") print("[*] Operation finished. Closing browser.") await browser_context.close()