feat: Add ability to rename jobs in the persistent queue

This commit is contained in:
Simon Cordt
2025-09-24 19:35:13 +02:00
parent 6bf0f4d2c7
commit c98537c15a
3 changed files with 109 additions and 0 deletions
+26
View File
@@ -128,6 +128,32 @@ class QueueDatabase:
with self._get_conn() as conn:
conn.execute('UPDATE queue_items SET priority = ? WHERE prompt_id = ?', (new_priority, prompt_id))
def update_job_name(self, prompt_id: str, new_name: str) -> bool:
"""Update the human-friendly name in the stored workflow JSON.
Attempts to set either workflow.name (nested) or top-level name, depending on structure.
Returns True on success, False if no such job.
"""
with self._get_conn() as conn:
cur = conn.execute('SELECT workflow FROM queue_items WHERE prompt_id = ? LIMIT 1', (prompt_id,))
row = cur.fetchone()
if not row:
return False
wf_text = row['workflow']
try:
wf = json.loads(wf_text) if isinstance(wf_text, str) else (wf_text or {})
except Exception:
wf = {}
# Prefer nested workflow.name if object contains a workflow field
if isinstance(wf, dict):
if isinstance(wf.get('workflow'), dict):
wf['workflow']['name'] = str(new_name)
else:
wf['name'] = str(new_name)
updated = json.dumps(wf) if wf is not None else None
conn.execute('UPDATE queue_items SET workflow = ? WHERE prompt_id = ?', (updated, prompt_id))
return True
def add_history(
self,
prompt_id: str,
+34
View File
@@ -273,6 +273,7 @@ class RoutesHelper:
web.post('/api/pqueue/reorder', manager._api_reorder),
web.patch('/api/pqueue/priority', manager._api_priority),
web.post('/api/pqueue/delete', manager._api_delete),
web.patch('/api/pqueue/rename', manager._api_rename),
]
self.app.add_routes(routes)
@@ -510,6 +511,39 @@ class PersistentQueueManager:
logging.warning(f"PersistentQueue delete failed: {e}")
return web.json_response({"ok": False, "error": str(e)}, status=400)
async def _api_rename(self, request: web.Request) -> web.Response:
try:
from server import PromptServer
body = await request.json()
prompt_id: str = body.get('prompt_id')
new_name: str = body.get('name')
if not prompt_id or new_name is None:
return web.json_response({"ok": False, "error": "prompt_id and name required"}, status=400)
# Persist to DB
ok = self.db.update_job_name(prompt_id, str(new_name))
if not ok:
return web.json_response({"ok": False, "error": "job not found"}, status=404)
# Update in-memory queue copy to reflect quickly
q = PromptServer.instance.prompt_queue
with q.mutex:
for idx, item in enumerate(list(q.queue)):
try:
if item[1] == prompt_id:
# item = (number, prompt_id, prompt, extra_data, outputs_to_execute)
prompt = item[2]
if isinstance(prompt, dict):
if isinstance(prompt.get('workflow'), dict):
prompt['workflow']['name'] = str(new_name)
else:
prompt['name'] = str(new_name)
q.queue[idx] = (item[0], item[1], prompt, item[3], item[4])
except Exception:
pass
return web.json_response({"ok": True})
except Exception as e:
logging.warning(f"PersistentQueue rename failed: {e}")
return web.json_response({"ok": False, "error": str(e)}, status=400)
def _rebuild_queue_by_prompt_ids(self, ordered_prompt_ids: List[str]) -> None:
from server import PromptServer
q = PromptServer.instance.prompt_queue
+49
View File
@@ -27,6 +27,12 @@
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt_ids }),
}),
rename: (prompt_id, name) =>
fetch("/api/pqueue/rename", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt_id, name }),
}),
};
const Icons = {
@@ -579,6 +585,49 @@
const status = UI.statusBadge(db.status || "pending", { subtle: true });
const primaryLabel = UI.buildWorkflowLabel(pid, item, db);
primaryLabel.contentEditable = "true";
primaryLabel.spellcheck = false;
primaryLabel.dataset.id = pid;
primaryLabel.dataset.original = primaryLabel.textContent || "";
primaryLabel.classList.add("pqueue-editable");
const finishEdit = async (node, save) => {
try {
const id = node?.dataset?.id;
if (!id) return;
const original = node.dataset.original || "";
const current = (node.textContent || "").trim();
if (!save) {
node.textContent = original;
return;
}
if (current === original || !current) return;
await API.rename(id, current);
state.workflowNameCache.set(id, current);
setStatusMessage("Name updated");
} catch (err) {
console.error("pqueue: rename failed", err);
state.error = err?.message || "Failed to rename";
UI.updateToolbarStatus();
} finally {
node.dataset.original = node.textContent || "";
}
};
primaryLabel.addEventListener("keydown", async (e) => {
e.stopPropagation();
const target = e.currentTarget;
if (e.key === "Enter") {
e.preventDefault();
await finishEdit(target, true);
target.blur();
} else if (e.key === "Escape") {
e.preventDefault();
await finishEdit(target, false);
target.blur();
}
});
primaryLabel.addEventListener("blur", async (e) => {
await finishEdit(e.currentTarget, true);
});
const line1 = UI.el("div", { class: "pqueue-pcard__line1" }, [
UI.el("div", { class: "pqueue-pcard__l1-left" }, [checkbox, primaryLabel]),
UI.el("div", { class: "pqueue-pcard__l1-right" }, [UI.el("span", { class: "pqueue-muted", text: db.created_at ? Format.relative(db.created_at) : "—" })])