211 lines
6.5 KiB
Python
211 lines
6.5 KiB
Python
from server import PromptServer
|
|
|
|
|
|
class AnyType(str):
|
|
"""
|
|
ComfyUI custom nodeでよく使われるANY型。
|
|
型チェック時に他の型と不一致扱いされないようにする。
|
|
"""
|
|
def __ne__(self, other):
|
|
return False
|
|
|
|
|
|
ANY_TYPE = AnyType("*")
|
|
|
|
def make_title_payload(label, running, pending, total):
|
|
return {
|
|
"label": label,
|
|
"running": running,
|
|
"pending": pending,
|
|
"total": total,
|
|
}
|
|
|
|
def get_queue_counts():
|
|
"""
|
|
/queue APIを叩かず、ComfyUI内部のPromptQueueから件数だけ読む。
|
|
大量キュー時でも軽くするため、巨大なqueue配列のJSON化はしない。
|
|
"""
|
|
server = getattr(PromptServer, "instance", None)
|
|
if server is None:
|
|
return 0, 0, 0
|
|
|
|
prompt_queue = getattr(server, "prompt_queue", None)
|
|
if prompt_queue is None:
|
|
return 0, 0, 0
|
|
|
|
# 最軽量ルート。
|
|
if all(hasattr(prompt_queue, name) for name in ("queue", "currently_running", "mutex")):
|
|
with prompt_queue.mutex:
|
|
pending = len(prompt_queue.queue)
|
|
running = len(prompt_queue.currently_running)
|
|
total = pending + running
|
|
return running, pending, total
|
|
|
|
# 保険。
|
|
if hasattr(prompt_queue, "get_tasks_remaining"):
|
|
total = prompt_queue.get_tasks_remaining()
|
|
return 0, total, total
|
|
|
|
# 最終手段。大量キュー時はやや重い。
|
|
if hasattr(prompt_queue, "get_current_queue_volatile"):
|
|
running_items, pending_items = prompt_queue.get_current_queue_volatile()
|
|
running = len(running_items)
|
|
pending = len(pending_items)
|
|
return running, pending, running + pending
|
|
|
|
return 0, 0, 0
|
|
|
|
|
|
def make_status(label, running, pending, total):
|
|
return f"{label}: running={running}, pending={pending}, total={total}"
|
|
|
|
|
|
def make_title(label, running, pending, total, title_format):
|
|
"""
|
|
折りたたみタイトル用の短い表示を作る。
|
|
total: queue: 500
|
|
short: queue: 1/499/500
|
|
full : queue: running=1, pending=499, total=500
|
|
"""
|
|
if title_format == "total":
|
|
return f"{label}: {total}"
|
|
|
|
if title_format == "short":
|
|
return f"{label}: {running}/{pending}/{total}"
|
|
|
|
return make_status(label, running, pending, total)
|
|
|
|
|
|
class LightQueueCounterAnyPassthrough:
|
|
"""
|
|
何でも受けて、何でもそのまま返す軽量キューカウンター。
|
|
画像、latent、conditioning、model、文字列など、任意の経路に差し込める。
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"value": (ANY_TYPE, {"forceInput": True}),
|
|
},
|
|
"optional": {
|
|
"label": ("STRING", {"default": "queue", "multiline": False}),
|
|
"title_format": (["total", "short", "full"], {"default": "total"}),
|
|
"print_to_console": ("BOOLEAN", {"default": True}),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = (ANY_TYPE, "STRING", "STRING", "INT", "INT", "INT")
|
|
RETURN_NAMES = ("value", "status", "title", "running", "pending", "total")
|
|
FUNCTION = "run"
|
|
CATEGORY = "utils/queue"
|
|
|
|
def run(self, value, label="queue", title_format="total", print_to_console=True):
|
|
running, pending, total = get_queue_counts()
|
|
|
|
status = make_status(label, running, pending, total)
|
|
title = make_title(label, running, pending, total, title_format)
|
|
|
|
if print_to_console:
|
|
print(f"[LightQueueCounter] {status}")
|
|
|
|
return (value, status, title, running, pending, total)
|
|
|
|
|
|
class LightQueueCounterSetTitle:
|
|
"""
|
|
文字列を受け取り、自分自身のノードタイトルに設定する表示用ノード。
|
|
AnyPassthrough の status または title を接続して使う。
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"title": ("STRING", {"forceInput": True}),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
RETURN_NAMES = ("title",)
|
|
FUNCTION = "run"
|
|
OUTPUT_NODE = True
|
|
CATEGORY = "utils/queue"
|
|
|
|
@classmethod
|
|
def IS_CHANGED(cls, title):
|
|
# 表示専用なので毎回実行させる。
|
|
return float("nan")
|
|
|
|
def run(self, title):
|
|
title = str(title).replace("\n", " ").strip()
|
|
|
|
return {
|
|
"ui": {
|
|
"light_queue_counter_title": [title],
|
|
"text": [title],
|
|
},
|
|
"result": (title,),
|
|
}
|
|
|
|
|
|
class LightQueueCounterTitleTap:
|
|
"""
|
|
任意の値をトリガーとして受け取り、キュー件数を読んで自分のタイトルに表示する。
|
|
メイン経路を変更したくない場合に枝分かれで置く。
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"trigger": (ANY_TYPE, {"forceInput": True}),
|
|
},
|
|
"optional": {
|
|
"label": ("STRING", {"default": "queue", "multiline": False}),
|
|
"title_format": (["total", "short", "full"], {"default": "total"}),
|
|
"print_to_console": ("BOOLEAN", {"default": False}),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ()
|
|
FUNCTION = "run"
|
|
OUTPUT_NODE = True
|
|
CATEGORY = "utils/queue"
|
|
|
|
@classmethod
|
|
def IS_CHANGED(cls, trigger, label="queue", title_format="total", print_to_console=False):
|
|
# 表示専用なので毎回実行させる。
|
|
return float("nan")
|
|
|
|
def run(self, trigger, label="queue", title_format="total", print_to_console=False):
|
|
running, pending, total = get_queue_counts()
|
|
|
|
status = make_status(label, running, pending, total)
|
|
title = make_title(label, running, pending, total, title_format)
|
|
payload = make_title_payload(label, running, pending, total)
|
|
|
|
if print_to_console:
|
|
print(f"[LightQueueCounter] {status}")
|
|
|
|
return {
|
|
"ui": {
|
|
"light_queue_counter_title": [title],
|
|
"light_queue_counter_payload": [payload],
|
|
"text": [status],
|
|
},
|
|
"result": (),
|
|
}
|
|
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"LightQueueCounterAnyPassthrough": LightQueueCounterAnyPassthrough,
|
|
"LightQueueCounterSetTitle": LightQueueCounterSetTitle,
|
|
"LightQueueCounterTitleTap": LightQueueCounterTitleTap,
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"LightQueueCounterAnyPassthrough": "Light Queue Counter Any Passthrough",
|
|
"LightQueueCounterSetTitle": "Light Queue Counter Set Title",
|
|
"LightQueueCounterTitleTap": "Light Queue Counter Title Tap",
|
|
} |