From 3e46b3363beed9ab1a3ad72c184de208d1ed3fa5 Mon Sep 17 00:00:00 2001 From: Karl Date: Tue, 12 Aug 2025 14:15:23 +0100 Subject: [PATCH] working queue logic --- libs/comfyui.py | 51 ++++++++++++++++++ routes/job_routes.py | 8 ++- templates/create_image.html | 101 +++++++++++++++++++++++++++++++++++- 3 files changed, 156 insertions(+), 4 deletions(-) diff --git a/libs/comfyui.py b/libs/comfyui.py index 9d61080..9911bd4 100644 --- a/libs/comfyui.py +++ b/libs/comfyui.py @@ -208,3 +208,54 @@ def get_queue_count() -> int: except Exception as e: logging.error(f"Error fetching queue count: {e}") return 0 + +def get_queue_details() -> list: + """Fetches detailed queue information including model names and prompts.""" + url = user_config["comfyui"]["comfyui_url"] + "/queue" + try: + response = requests.get(url) + response.raise_for_status() + data = response.json() + jobs = [] + for job_list in [data.get("queue_running", []), data.get("queue_pending", [])]: + for job in job_list: + # Extract prompt data (format: [priority, time, prompt]) + prompt_data = job[2] + model = "Unknown" + prompt = "No prompt" + + # Find model loader node (works for SDXL/FLUX/Qwen workflows) + for node in prompt_data.values(): + if node.get("class_type") in ["CheckpointLoaderSimple", "UnetLoaderGGUFAdvancedDisTorchMultiGPU"]: + model = node["inputs"].get("ckpt_name", "Unknown") + break + + # Find prompt node using class_type pattern and title matching + for node in prompt_data.values(): + class_type = node.get("class_type", "") + if "CLIPTextEncode" in class_type and "text" in node["inputs"]: + meta = node.get('_meta', {}) + title = meta.get('title', '').lower() + if 'positive' in title or 'prompt' in title: + prompt = node["inputs"]["text"] + break + + jobs.append({ + "id": job[0], + "model": model.split(".")[0] if model != "Unknown" else model, + "prompt": prompt + }) + return jobs + except Exception as e: + logging.error(f"Error fetching queue details: {e}") + return [] + try: + response = requests.get(url) + response.raise_for_status() + data = response.json() + pending = len(data.get("queue_pending", [])) + running = len(data.get("queue_running", [])) + return pending + running + except Exception as e: + logging.error(f"Error fetching queue count: {e}") + return 0 diff --git a/routes/job_routes.py b/routes/job_routes.py index 6fec8d6..9766619 100644 --- a/routes/job_routes.py +++ b/routes/job_routes.py @@ -1,8 +1,12 @@ -from flask import Blueprint -from libs.comfyui import cancel_current_job +from flask import Blueprint, jsonify +from libs.comfyui import cancel_current_job, get_queue_details bp = Blueprint("job_routes", __name__) @bp.route("/cancel", methods=["GET"]) def cancel_job(): return cancel_current_job() + +@bp.route("/api/queue", methods=["GET"]) +def api_queue(): + return jsonify(get_queue_details()) diff --git a/templates/create_image.html b/templates/create_image.html index 9c246d4..5841ac0 100644 --- a/templates/create_image.html +++ b/templates/create_image.html @@ -131,12 +131,54 @@ height: 150px; } } + .queue-dropdown { + position: absolute; + top: 100%; + right: 0; + background: #222; + border: 1px solid #444; + border-radius: 5px; + padding: 10px; + z-index: 1001; + display: none; + max-height: 300px; + overflow-y: auto; + width: 300px; + } + + .queue-item { + margin-bottom: 5px; + padding: 5px; + border-bottom: 1px solid #333; + } + + .queue-item:last-child { + border-bottom: none; + } + + .queue-item .model { + font-weight: bold; + color: #00aaff; + } + + .queue-item .prompt { + font-size: 0.9em; + color: #aaa; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } {% endblock %} {% block content %} -
- Queue: {{ queue_count | default(0) }} +
+ +
+ +

Create An Image

@@ -272,5 +314,60 @@ alert("Error requesting random prompt: " + error); }); } + document.addEventListener('DOMContentLoaded', function() { + const queueBtn = document.getElementById('queue-btn'); + const queueDropdown = document.getElementById('queue-dropdown'); + const queueCountSpan = document.getElementById('queue-count'); + + // Toggle dropdown visibility + queueBtn.addEventListener('click', function(e) { + e.stopPropagation(); + if (queueDropdown.style.display === 'block') { + queueDropdown.style.display = 'none'; + } else { + fetchQueueDetails(); + queueDropdown.style.display = 'block'; + } + }); + + // Close dropdown when clicking outside + document.addEventListener('click', function() { + queueDropdown.style.display = 'none'; + }); + + // Prevent dropdown from closing when clicking inside it + queueDropdown.addEventListener('click', function(e) { + e.stopPropagation(); + }); + + function fetchQueueDetails() { + fetch('/api/queue') + .then(response => response.json()) + .then(jobs => { + queueCountSpan.textContent = jobs.length; + const container = queueDropdown; + container.innerHTML = ''; + + if (jobs.length === 0) { + container.innerHTML = '
No jobs in queue
'; + return; + } + + jobs.forEach(job => { + const item = document.createElement('div'); + item.className = 'queue-item'; + item.innerHTML = ` +
${job.model}
+
${job.prompt}
+ `; + container.appendChild(item); + }); + }) + .catch(error => { + console.error('Error fetching queue:', error); + queueDropdown.innerHTML = '
Error loading queue
'; + }); + } + }); {% endblock %} \ No newline at end of file