Files

117 lines
5.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Isolated Gallery preview. NOT a ComfyUI inference server.
python scripts/preview.py --port 8189
Creates demo images/state in a temporary directory, never in real ComfyUI paths.
Bind to localhost outside a sandbox; --host 0.0.0.0 is only for controlled preview.
"""
from __future__ import annotations
import argparse
import json
import os
from pathlib import Path
import random
import re
import sys
import tempfile
import time
import types
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--host", default="127.0.0.1")
parser.add_argument("--port", type=int, default=8189)
parser.add_argument("--trash-items", type=int, default=0, help="Seed up to 500 disposable trash fixtures for bulk-action QA")
parser.add_argument("--image-count", type=int, default=18, help="Number of procedural images for scrolling QA (1–500)")
args = parser.parse_args()
if not 0 <= args.trash_items <= 500:
parser.error("--trash-items must be between 0 and 500")
if not 1 <= args.image_count <= 500:
parser.error("--image-count must be between 1 and 500")
root = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(root))
temporary = tempfile.TemporaryDirectory(prefix="universal-preview-")
base = Path(temporary.name)
for name in ("output", "input", "data"):
(base / name).mkdir()
os.environ["UNIVERSAL_EXTRACTOR_DATA_DIR"] = str(base / "data")
folder_paths = types.ModuleType("folder_paths")
folder_paths.base_path = str(base)
folder_paths.get_output_directory = lambda: str(base / "output")
folder_paths.get_input_directory = lambda: str(base / "input")
sys.modules["folder_paths"] = folder_paths
from PIL import Image, ImageDraw, PngImagePlugin
from aiohttp import web
from py.gallery import routes
palettes = [
("#efe9df", "#c3c6b3", "#677c68", "#ddd2b7"),
("#e9ddd3", "#dba890", "#ad705b", "#f2cfc0"),
("#dce4e7", "#b0c5c8", "#556f7c", "#d4d6c0"),
("#efe8d8", "#d7c09b", "#958267", "#e0c697"),
]
folders = ["01 · Landscapes", "02 · Color studies", "03 · Architecture"]
for folder in folders:
(base / "output" / folder).mkdir()
for index in range(args.image_count):
width, height = [(600, 760), (600, 600), (600, 460)][index % 3]
colors = palettes[index % len(palettes)]
image = Image.new("RGB", (width, height), colors[0])
draw = ImageDraw.Draw(image)
rng = random.Random(index)
if index % 3 == 0:
draw.ellipse((360, 100, 460, 200), fill=colors[3])
for layer in range(3):
y = height * (.44 + .17 * layer)
points = [(0, height), (0, y)] + [(x, y + rng.randrange(-65, 65)) for x in range(0, 660, 60)] + [(width, height)]
draw.polygon(points, fill=colors[(layer + 1) % 4])
elif index % 3 == 1:
for ring in range(8, 0, -1):
radius = ring * 32
draw.ellipse((300-radius, 300-radius, 300+radius, 300+radius), fill=colors[ring % 4])
else:
draw.rectangle((120, 110, 480, 460), fill=colors[1])
draw.rounded_rectangle((215, 160, 385, 500), radius=85, fill=colors[2])
draw.rectangle((0, 370, 600, 460), fill=colors[3])
draw.polygon([(390, 280), (600, 390), (600, 460), (390, 460)], fill=colors[2])
prompt = f"Demo study {index+1}, minimal composition, soft daylight"
graph = {"1": {"class_type": "CLIPTextEncode", "inputs": {"text": prompt}},
"2": {"class_type": "KSampler", "inputs": {"steps": 24, "cfg": 6, "seed": index, "positive": ["1", 0]}}}
metadata = PngImagePlugin.PngInfo()
metadata.add_text("prompt", json.dumps(graph))
image.save(base / "output" / folders[index % 3] / f"Study_{index+1:03}.png", pnginfo=metadata)
(base / "data" / "Demo prompts.json").write_text(json.dumps([
{"name": "Soft daylight", "tags": ["natural light", "minimal composition"]},
{"name": "Quiet landscapes", "tags": ["landscape", "muted colors"]},
], ensure_ascii=False))
if args.trash_items:
from py.gallery import service
fixture_names = [f"QA_Disposable_{index:04}.png" for index in range(args.trash_items)]
for name in fixture_names:
Image.new("RGB", (16, 16), "#c3c6b3").save(base / "output" / name)
service.delete_images(fixture_names)
async def preview_updates(force=False):
from py.gallery.update_checker import _build_status
return {**_build_status([]), "check_status": "preview", "last_success_at": None}
routes.check_update_status = preview_updates
app = web.Application()
routes.register_routes(app)
# ComfyUI normally provides /view. The isolated fixture supplies it only for its own samples.
async def preview_image(request):
kind = request.query.get("type", "output")
if kind not in ("input", "output"):
raise web.HTTPNotFound()
image_root = (base / kind).resolve()
image_path = (image_root / request.query.get("subfolder", "") / request.query.get("filename", "")).resolve()
if not image_path.is_relative_to(image_root) or not image_path.is_file():
raise web.HTTPNotFound()
return web.FileResponse(image_path)
app.router.add_get("/view", preview_image)
async def home(request):
raise web.HTTPFound("/gallery/")
app.router.add_get("/", home)
print("ISOLATED DEMO: procedural sample images, no ComfyUI/GPU inference; state is temporary.")
web.run_app(app, host=args.host, port=args.port)