Initial commit
This commit is contained in:
commit
73d16c9b69
26 files changed
+1660
No files matched your search
@@ -0,0 +1,188 @@
|
||||
import requests
|
||||
import json
|
||||
import uuid
|
||||
import time
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
COMFY_PORT = 8188
|
||||
POLL_TIMEOUT = 300
|
||||
|
||||
MODELS = {
|
||||
"lineart": {
|
||||
"name": "Deliberate_v2.safetensors",
|
||||
"url": "https://huggingface.co/XpucT/Deliberate/resolve/main/Deliberate_v2.safetensors",
|
||||
},
|
||||
"manga": {
|
||||
"name": "Deliberate_v2.safetensors",
|
||||
"url": "https://huggingface.co/XpucT/Deliberate/resolve/main/Deliberate_v2.safetensors",
|
||||
},
|
||||
}
|
||||
|
||||
DEFAULT_MODEL_URL = MODELS["manga"]["url"]
|
||||
DEFAULT_MODEL_NAME = MODELS["manga"]["name"]
|
||||
|
||||
def base_url(host="127.0.0.1"):
|
||||
return f"http://{host}:{COMFY_PORT}"
|
||||
|
||||
def is_ready(host="127.0.0.1", timeout=3):
|
||||
try:
|
||||
requests.get(f"{base_url(host)}/system_stats", timeout=timeout)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def find_model(comfy_path, name=None):
|
||||
model_dir = os.path.join(comfy_path, "models", "checkpoints")
|
||||
if not os.path.isdir(model_dir):
|
||||
return None
|
||||
if name:
|
||||
return name if os.path.exists(os.path.join(model_dir, name)) else None
|
||||
for f in os.listdir(model_dir):
|
||||
if f.endswith(".safetensors") or f.endswith(".ckpt"):
|
||||
return f
|
||||
return None
|
||||
|
||||
def download_model(comfy_path, style="manga"):
|
||||
model_info = MODELS.get(style, MODELS["manga"])
|
||||
model_name = model_info["name"]
|
||||
model_url = model_info["url"]
|
||||
style_label = "Anything V5 (anime/manga lineart)" if style == "lineart" else "Deliberate v2 (general purpose)"
|
||||
|
||||
model_dir = os.path.join(comfy_path, "models", "checkpoints")
|
||||
os.makedirs(model_dir, exist_ok=True)
|
||||
dest = os.path.join(model_dir, model_name)
|
||||
|
||||
if os.path.exists(dest):
|
||||
return model_name
|
||||
|
||||
print(f"\n Model for --style {style} not found: {model_name}")
|
||||
print(f" Recommended model: {style_label}")
|
||||
print(f" Source: {model_url}")
|
||||
confirm = input("\n Download it now? (~2GB) [Y/n] ").strip().lower()
|
||||
if confirm in ("n", "no"):
|
||||
print(" Aborted.")
|
||||
sys.exit(0)
|
||||
|
||||
print(f"\n Downloading {model_name}...")
|
||||
|
||||
bar_width = 35
|
||||
with requests.get(model_url, stream=True) as r:
|
||||
r.raise_for_status()
|
||||
total = int(r.headers.get("content-length", 0))
|
||||
downloaded = 0
|
||||
with open(dest, "wb") as f:
|
||||
for chunk in r.iter_content(chunk_size=1024 * 1024):
|
||||
f.write(chunk)
|
||||
downloaded += len(chunk)
|
||||
if total:
|
||||
pct = downloaded / total
|
||||
filled = int(bar_width * pct)
|
||||
bar = "\u2588" * filled + "\u2591" * (bar_width - filled)
|
||||
mb_done = downloaded / 1024 / 1024
|
||||
mb_total = total / 1024 / 1024
|
||||
print(f"\r [{bar}] {mb_done:.0f}/{mb_total:.0f} MB ", end="", flush=True)
|
||||
|
||||
print(f"\r Download complete: {dest} ")
|
||||
return model_name
|
||||
|
||||
def ensure_model_for_style(comfy_path, style="manga"):
|
||||
model_info = MODELS.get(style, MODELS["manga"])
|
||||
model_name = model_info["name"]
|
||||
existing = find_model(comfy_path, name=model_name)
|
||||
if existing:
|
||||
return existing
|
||||
return download_model(comfy_path, style=style)
|
||||
|
||||
def install_dependencies(comfy_path):
|
||||
req_file = os.path.join(comfy_path, "requirements.txt")
|
||||
stamp = os.path.join(comfy_path, ".deps_installed")
|
||||
if not os.path.exists(req_file):
|
||||
return
|
||||
if os.path.exists(stamp):
|
||||
req_mtime = os.path.getmtime(req_file)
|
||||
stamp_mtime = os.path.getmtime(stamp)
|
||||
if stamp_mtime >= req_mtime:
|
||||
return
|
||||
print(" Installing ComfyUI dependencies...")
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "pip", "install", "-r", req_file, "--quiet"],
|
||||
check=True,
|
||||
)
|
||||
open(stamp, "w").close()
|
||||
|
||||
def build_workflow(positive, negative, model_name, steps=20, width=768, height=1024, cfg=4.5):
|
||||
seed = int(time.time()) % 2**32
|
||||
return {
|
||||
"3": {
|
||||
"class_type": "KSampler",
|
||||
"inputs": {
|
||||
"seed": seed,
|
||||
"steps": steps,
|
||||
"cfg": cfg,
|
||||
"sampler_name": "euler_ancestral",
|
||||
"scheduler": "karras",
|
||||
"denoise": 1.0,
|
||||
"model": ["4", 0],
|
||||
"positive": ["6", 0],
|
||||
"negative": ["7", 0],
|
||||
"latent_image": ["5", 0],
|
||||
},
|
||||
},
|
||||
"4": {
|
||||
"class_type": "CheckpointLoaderSimple",
|
||||
"inputs": {"ckpt_name": model_name},
|
||||
},
|
||||
"5": {
|
||||
"class_type": "EmptyLatentImage",
|
||||
"inputs": {"width": width, "height": height, "batch_size": 1},
|
||||
},
|
||||
"6": {
|
||||
"class_type": "CLIPTextEncode",
|
||||
"inputs": {"text": positive, "clip": ["4", 1]},
|
||||
},
|
||||
"7": {
|
||||
"class_type": "CLIPTextEncode",
|
||||
"inputs": {"text": negative, "clip": ["4", 1]},
|
||||
},
|
||||
"8": {
|
||||
"class_type": "VAEDecode",
|
||||
"inputs": {"samples": ["3", 0], "vae": ["4", 2]},
|
||||
},
|
||||
"9": {
|
||||
"class_type": "SaveImage",
|
||||
"inputs": {"filename_prefix": "manga", "images": ["8", 0]},
|
||||
},
|
||||
}
|
||||
|
||||
def generate_image(base_url_str, positive, negative, model_name, steps=20, width=768, height=1024, cfg=4.5):
|
||||
client_id = str(uuid.uuid4())
|
||||
workflow = build_workflow(positive, negative, model_name, steps, width, height, cfg)
|
||||
|
||||
r = requests.post(f"{base_url_str}/prompt", json={"prompt": workflow, "client_id": client_id})
|
||||
r.raise_for_status()
|
||||
prompt_id = r.json()["prompt_id"]
|
||||
|
||||
deadline = time.time() + POLL_TIMEOUT
|
||||
while time.time() < deadline:
|
||||
time.sleep(1)
|
||||
hist = requests.get(f"{base_url_str}/history/{prompt_id}").json()
|
||||
if prompt_id in hist:
|
||||
outputs = hist[prompt_id]["outputs"]
|
||||
for node_id, node_output in outputs.items():
|
||||
if "images" in node_output:
|
||||
img_info = node_output["images"][0]
|
||||
img_r = requests.get(
|
||||
f"{base_url_str}/view",
|
||||
params={
|
||||
"filename": img_info["filename"],
|
||||
"subfolder": img_info.get("subfolder", ""),
|
||||
"type": img_info["type"],
|
||||
},
|
||||
)
|
||||
img_r.raise_for_status()
|
||||
return img_r.content
|
||||
break
|
||||
|
||||
raise RuntimeError(f"ComfyUI: no images returned for prompt_id {prompt_id} within {POLL_TIMEOUT}s")
|
||||
Reference in new issue
Block a user