From b0952e0fb3c39623ee5eeacc67fa5bc27f4dff2b Mon Sep 17 00:00:00 2001 From: Ivor Strelar Date: Mon, 21 Sep 2026 13:40:42 +0200 Subject: [PATCH] Revert "fix: drop the HTTP endpoint that left 1.0.8 pending registry review" Removing the route did not clear the Pending status: 1.0.9 shipped without it and sits Pending alongside 1.0.8, so the endpoint was never the cause. Pending appears to be the normal post-upload state before a scan promotes a version to Active. Restores the Refresh button and its preview endpoint. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 2 + py/sf_google_sheet_prompt_queue.py | 30 ++++++++++- pyproject.toml | 2 +- web/js/sf_google_sheet_prompt_queue.js | 75 ++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 web/js/sf_google_sheet_prompt_queue.js diff --git a/README.md b/README.md index de26a90..4685e7c 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,8 @@ and skips row 2. Each prompt triggers a separate pass through the downstream gra - **Scanning stops at the first empty prompt cell**, so anything below a gap is ignored. - **The sheet is re-read on every run**, so edits you make in the browser are picked up automatically. Add rows or clear status cells and just hit Run again. +- **Refresh sheet** is optional — it reports how many prompts are pending without running + the graph. --- diff --git a/py/sf_google_sheet_prompt_queue.py b/py/sf_google_sheet_prompt_queue.py index aa88e00..a4676da 100644 --- a/py/sf_google_sheet_prompt_queue.py +++ b/py/sf_google_sheet_prompt_queue.py @@ -157,7 +157,8 @@ all. Write `done` next to a prompt in the sheet and the next run skips it; clear the cell and it gets queued again. Scanning stops at the first empty prompt cell. The sheet is re-read fresh on every run, so edits you make in the browser are -picked up automatically — there is nothing to refresh by hand. +always picked up. **Refresh sheet** is optional — it reports how many prompts +are pending without having to run the graph. """ @classmethod @@ -197,3 +198,30 @@ picked up automatically — there is nothing to refresh by hand. NODE_CLASS_MAPPINGS = {"SFGoogleSheetPromptQueue": SFGoogleSheetPromptQueue} NODE_DISPLAY_NAME_MAPPINGS = {"SFGoogleSheetPromptQueue": "SF Google Sheet Prompt Queue"} + + +# --- Server route backing the node's Refresh button --------------------------- + +try: + from aiohttp import web + from server import PromptServer + + @PromptServer.instance.routes.post("/sf_gsheet/preview") + async def _preview(request): + body = await request.json() + try: + rows = fetch_rows(body.get("url", ""), body.get("sheet_gid", "0")) + pending, total = scan_rows( + rows, + col_letter_to_index(body.get("prompt_col", "A")), + col_letter_to_index(body.get("status_col", "B")), + bool(body.get("skip_first_row", True)), + ) + return web.json_response( + {"ok": True, "total": total, "pending": len(pending)} + ) + except Exception as e: + return web.json_response({"ok": False, "error": str(e)}) + +except ImportError: + pass diff --git a/pyproject.toml b/pyproject.toml index f8f8dcb..ee51e71 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui-sf-nodes" description = "Custom nodes for ComfyUI including LLM Chat with support for Claude, Gemini, and OpenAI models, WaveSpeed AI image/video generation, VertexAI integration, and workflow utilities" -version = "1.0.9" +version = "1.0.10" license = { file = "LICENSE" } dependencies = [ "requests>=2.31.0", diff --git a/web/js/sf_google_sheet_prompt_queue.js b/web/js/sf_google_sheet_prompt_queue.js new file mode 100644 index 0000000..b446444 --- /dev/null +++ b/web/js/sf_google_sheet_prompt_queue.js @@ -0,0 +1,75 @@ +/** + * SF Google Sheet Prompt Queue — adds a Refresh button that re-reads the sheet + * and reports how many prompts are still waiting to run. + */ + +import { app } from "../../../scripts/app.js"; +import { api } from "../../../scripts/api.js"; + +const NODE_NAME = "SFGoogleSheetPromptQueue"; + +function widgetValue(node, name, fallback) { + const widget = node.widgets?.find((w) => w.name === name); + return widget ? widget.value : fallback; +} + +app.registerExtension({ + name: "Stillfront.GoogleSheetPromptQueue", + + async beforeRegisterNodeDef(nodeType, nodeData) { + if (nodeData.name !== NODE_NAME) { + return; + } + + const onNodeCreated = nodeType.prototype.onNodeCreated; + nodeType.prototype.onNodeCreated = function () { + const result = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined; + const node = this; + + const status = node.addWidget("text", "", "Press Refresh to read the sheet", () => {}); + status.disabled = true; + status.serialize = false; + + const setStatus = (text) => { + status.value = text; + node.setDirtyCanvas(true, false); + }; + + const refreshButton = node.addWidget("button", "Refresh sheet", null, async () => { + setStatus("Reading sheet…"); + try { + const response = await api.fetchApi("/sf_gsheet/preview", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + url: widgetValue(node, "url", ""), + sheet_gid: widgetValue(node, "sheet_gid", "0"), + prompt_col: widgetValue(node, "prompt_col", "A"), + status_col: widgetValue(node, "status_col", "B"), + skip_first_row: widgetValue(node, "skip_first_row", true), + }), + }); + const data = await response.json(); + + if (!data.ok) { + setStatus(`Error: ${data.error}`); + return; + } + + const cap = widgetValue(node, "max_rows", 10); + const willSend = Math.min(data.pending, cap); + setStatus( + data.pending === 0 + ? `Nothing pending — all ${data.total} prompt(s) marked` + : `${data.pending} pending of ${data.total} — next run sends ${willSend}` + ); + } catch (e) { + setStatus("Could not reach ComfyUI server"); + } + }); + refreshButton.serialize = false; + + return result; + }; + }, +});