Files

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",
}